py-tgcalls 2.1.0rc2__py3-none-any.whl → 2.1.0rc4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: py-tgcalls
3
- Version: 2.1.0rc2
3
+ Version: 2.1.0rc4
4
4
  Summary: Async client API for the Telegram Calls.
5
5
  Author-email: Laky-64 <iraci.matteo@gmail.com>
6
6
  License: GNU LESSER GENERAL PUBLIC LICENSE
@@ -187,7 +187,7 @@ Requires-Python: >=3.9
187
187
  Description-Content-Type: text/markdown
188
188
  License-File: LICENSE
189
189
  Requires-Dist: aiohttp>=3.9.3
190
- Requires-Dist: ntgcalls<1.4.0,>=1.3.0b10
190
+ Requires-Dist: ntgcalls<1.4.0,>=1.3.0b14
191
191
  Requires-Dist: deprecation
192
192
  Provides-Extra: pyrogram
193
193
  Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
@@ -1,5 +1,5 @@
1
1
  pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
2
- pytgcalls/__version__.py,sha256=WU7MaPYaL-4qk-Vw8tQb_V5TySok6KjTjFI9Q3dw75k,26
2
+ pytgcalls/__version__.py,sha256=bQyjq0CUvIVmXyMKMECcV2-QIaZZRwtmrdC0uwmXqC4,26
3
3
  pytgcalls/environment.py,sha256=ctCHACvG6l8SdpPewSBhOvc70kbwpv18maC0TwLvZ08,1924
4
4
  pytgcalls/exceptions.py,sha256=0MmAktc53ajYAc7ThjD2tJ9PDyibUi0iHZMfUy2IoKs,4109
5
5
  pytgcalls/ffmpeg.py,sha256=tm6DBxyNfPh3h3an-b2s9x1UyX-cvkCdov9prlXxVZY,8649
@@ -8,7 +8,7 @@ pytgcalls/mtproto_required.py,sha256=6B-31p5qH_6oekUgypV4nK3hqPS6Nr-pA8S81wjnbaY
8
8
  pytgcalls/mutex.py,sha256=Frjji5Ctzlk4AXEBuBLnDK-7HbtreoV6zuyKpFpMNI4,236
9
9
  pytgcalls/pytgcalls.py,sha256=oBcWgBwusnXmjHrLEE99VVXARReVyrXdn9SyeBWHbVo,1479
10
10
  pytgcalls/pytgcalls_session.py,sha256=_BGJWvf7t3mki2DhlEPjh9cypvYuSFkMSxzTsfepwUk,2719
11
- pytgcalls/scaffold.py,sha256=cOG1_-MtGhBuyJmUXPmThC0VUF65TmdwnMCbgIozJoY,1159
11
+ pytgcalls/scaffold.py,sha256=SwePuT9V8LdOFy0KRlAnRSbsB2zeGQ4fs9ilrq7HZYI,1353
12
12
  pytgcalls/statictypes.py,sha256=CdlqgQNhTZ_uTE8-B8m01fJ7TlD2B42EI2QBPxDdAtA,3842
13
13
  pytgcalls/sync.py,sha256=IsOH3TD7cxUg_-zdGt12HoS8sBlXvcGayPZAoxxKM48,3396
14
14
  pytgcalls/version_manager.py,sha256=egeGgvb66zWlLTMuw2U-b0x8MfnRzMm1xAEVN87HF5c,296
@@ -27,29 +27,32 @@ pytgcalls/methods/__init__.py,sha256=hk1blAT5u_Isemdrg0nqInLsdRzTTZnak5NdAfkBPAk
27
27
  pytgcalls/methods/calls/__init__.py,sha256=xg4DZZClEnxwaj-DAq3e8gSR-g-MiYBdUEBth64lSXA,214
28
28
  pytgcalls/methods/calls/change_volume_call.py,sha256=viA3yHVxPJ421yE1dfFTh-kNEFTxMlaVFxVjC-PeX-0,719
29
29
  pytgcalls/methods/calls/get_participants.py,sha256=gKUAzvha1RpvbtSI_n-GlBQkOONxzibCaCr4aA17Mag,567
30
- pytgcalls/methods/calls/leave_call.py,sha256=dJg5JWmbTIKQJsCccvea4Ma-SaSKVZcRVRqkmsWOLx8,1376
30
+ pytgcalls/methods/calls/leave_call.py,sha256=ay07za5mNKmXGzmpeU6gJJmHVrLeXRtRmFT4QP94pGA,1475
31
31
  pytgcalls/methods/decorators/__init__.py,sha256=TCGaEVZnHjtOwv-3PNfaCVm0kyFhJApUPUNntt6MwyM,78
32
32
  pytgcalls/methods/decorators/on_update.py,sha256=ZTL4YcQk0N4Ru56a5WItUvkSN5SAqr6_RDZvXmZMIHs,316
33
33
  pytgcalls/methods/stream/__init__.py,sha256=dBx5cqvVyvhqykIuT_2rYCAvIzuPM1J4Hh4sZ083UXU,412
34
34
  pytgcalls/methods/stream/mute_stream.py,sha256=auo2aAazfEC90Ab6MzaiPdddiJ1w4fN_9HaORkAeOBY,570
35
35
  pytgcalls/methods/stream/pause_stream.py,sha256=z_AIWABrQMHmTwvlah_PrH9EjXbro8gKxZni4Km5ICg,573
36
- pytgcalls/methods/stream/play.py,sha256=1NiAIKBdSsiighc74EKHQv_Fl_ehWLYgGPq5x62s0sE,10189
37
- pytgcalls/methods/stream/record.py,sha256=toW1LtgUMiaw-KGe9DnWTRsGYLhoKlG0r8eJphMhGIA,1212
36
+ pytgcalls/methods/stream/play.py,sha256=-Ld5TQWLhU_ktKrb7q1Xrym36UGrWhaenIbjj-3mM3k,7524
37
+ pytgcalls/methods/stream/record.py,sha256=28mFK2azDv6gUziwcj4ybvrYka9P9VXnUXnuAa1aQ5w,1466
38
38
  pytgcalls/methods/stream/resume_stream.py,sha256=z_DgP4cDExjEqEeX_ZL--50MXQ9lrATK876SIwE71PQ,576
39
39
  pytgcalls/methods/stream/send_frame.py,sha256=Kj9R8OqUM-g7pt-FiWP-US7sCFkH5ciPr9S8v-WPtLg,1062
40
40
  pytgcalls/methods/stream/time.py,sha256=5y9TMBf_d6YPLbMcGx3yMZQUZdo8zb5fQb9STsh7R3Y,656
41
41
  pytgcalls/methods/stream/unmute_stream.py,sha256=KUMhfMbhsPmZsmpF4cGWC1FVW7YwXha2MmQnqrBhM8s,576
42
- pytgcalls/methods/utilities/__init__.py,sha256=JcKwqNo6fFXXfuab94fNEraKF1P9fnSSgr0WQDRjF2w,339
42
+ pytgcalls/methods/utilities/__init__.py,sha256=HHAkTQEX_23uwo1fxnCIoE2rLBm7fKSQa52SwBLudTI,522
43
43
  pytgcalls/methods/utilities/cache_peer.py,sha256=Ylt0wCCJOoNKf1wZEXjfE8aBZKUIIgdRUFOMTGA5DfE,140
44
44
  pytgcalls/methods/utilities/call_holder.py,sha256=MhIbwCG6DROd9_bHGa6aqu-rB0y4sngzPBj82zLtAXU,1068
45
45
  pytgcalls/methods/utilities/compose.py,sha256=Nzdv8orMmka5NIBZ1SW1nsqXRzArZl4m6FdZU7syaR4,334
46
46
  pytgcalls/methods/utilities/cpu_usage.py,sha256=Mbga4MFCIwuh7WC8sqBbv1Pa6ALcp5AIDyfYMH_Bix4,162
47
47
  pytgcalls/methods/utilities/idle.py,sha256=MDdzHTv1ws2yBhsvhBUnssGdghkZ2KwR0HUCPOwV38o,814
48
+ pytgcalls/methods/utilities/join_presentation.py,sha256=HLpVjhDkbMB6mIsCwdaGy-Ct7IaV44fM5-zOQ9M7bLU,1551
49
+ pytgcalls/methods/utilities/log_retries.py,sha256=6nD9J3350t82I0PKzK1pVx3ZaCBHATReiXYMs3PuVhQ,342
48
50
  pytgcalls/methods/utilities/ping.py,sha256=hhIMSHk2BzMB-IKpwLdZFVrsEvGm2ftJwKLs1k4anh8,244
49
51
  pytgcalls/methods/utilities/resolve_chat_id.py,sha256=92x2LHbUlnJMm-kS3fXOYmzYpY2TZbqtQD2rw3eBXDY,382
50
52
  pytgcalls/methods/utilities/run.py,sha256=cnYQd2xB5Cr_WS0Q2cXJZPGiN6JOCULzj1r4xXVyrlg,152
51
- pytgcalls/methods/utilities/start.py,sha256=b3S3x5GpCL0FTisUysVlkmg_AvJqqSD2bFO_ybn-3tQ,13529
52
- pytgcalls/methods/utilities/stream_params.py,sha256=WfKffrxMDoDLb8k7CB3n5IuN4wo6PebGEX0rLP3OaLk,2883
53
+ pytgcalls/methods/utilities/start.py,sha256=unc-O5XsUztJY7t7wu40F0uSthgq9YutcXRqcTTzhhA,14393
54
+ pytgcalls/methods/utilities/stream_params.py,sha256=OE673Bt1CiyCBFQGMPtC05zv3nObP3zirrh4OD9USdg,3109
55
+ pytgcalls/methods/utilities/update_sources.py,sha256=ISF6u3rk4IcVrPOEOB-uZUDfnwUp2_y1_2g9GboXpWM,1562
53
56
  pytgcalls/mtproto/__init__.py,sha256=X4zvzFG7km7qHyE0fdvA550WcOVO_xl_p__gvIfDGmw,130
54
57
  pytgcalls/mtproto/bridged_client.py,sha256=hktnfpfBK7PEL2n7Y0kBZzI7dkLlsdrDwkYliZki8zQ,5915
55
58
  pytgcalls/mtproto/client_cache.py,sha256=Mt0827e_T8DXJHOTkXhkIQUT9EUBWjoLcFcXP1gBnZY,5973
@@ -67,11 +70,12 @@ pytgcalls/types/participant_list.py,sha256=LmGjU63MK1v3SS2_4xNbk04OOjmukNdAXYLRn
67
70
  pytgcalls/types/py_object.py,sha256=VlazuMP0cFpExKimW8BtWR66LDewnNdQSLC7r_t7JIM,842
68
71
  pytgcalls/types/update.py,sha256=wPCzWLhrsScZ3ksRTyt8IuDaaG5YI-ItG_Yw-OqzK2Y,157
69
72
  pytgcalls/types/user_agent.py,sha256=sSfeGqUe0v0wqBgdVszNFK0iOC_0Tdyto9CglBXlY4U,1086
70
- pytgcalls/types/calls/__init__.py,sha256=6O2wp7I3d2YGLNeMgXTRyG37Y6BESafneLdr9n37_4s,346
73
+ pytgcalls/types/calls/__init__.py,sha256=f0zMKm_mwvNsDRPgs8IopuCEefU3V0-kuHAFsTMBoAc,403
71
74
  pytgcalls/types/calls/call.py,sha256=n7LW7FRNT2qJzbYC3D312judOlVUuocQ1eW9l9scGCo,546
72
75
  pytgcalls/types/calls/call_config.py,sha256=b6P43YTGF2t7E2CyD1mSYPJDUBvYYeHoxB3hSbTVyOY,120
73
76
  pytgcalls/types/calls/call_data.py,sha256=-qPj2QhWv32Xs7LyFQY4hiWDqJ21B8VBvdzREK8bDvY,544
74
77
  pytgcalls/types/calls/call_protocol.py,sha256=OVIQs1VgdY-DWbZbNr41hjLA4pGQvHx8Rgom1_NhJxQ,408
78
+ pytgcalls/types/calls/call_sources.py,sha256=sBhumPgEaN8uAKjBwb1Zf_Ag0qrceti2mURXqMhBusg,107
75
79
  pytgcalls/types/calls/group_call_config.py,sha256=auKH-hZJWj8PhTkyeQ_VK2z9NpNvNC7Scl_IhEUMnQM,353
76
80
  pytgcalls/types/calls/raw_call_update.py,sha256=hpNw6HrTW8Z36Lh2HinS-wzprryRtsIxyIFbIfjGgeI,795
77
81
  pytgcalls/types/chats/__init__.py,sha256=v8pUp_vbr2kQpyHtAQc80N-YqzmXHe9SbllUsa6njkU,261
@@ -90,13 +94,13 @@ pytgcalls/types/stream/device.py,sha256=EdoDg6lPE7fgoZI04Nr0E9zbIk-iRIBgYYAzVqoC
90
94
  pytgcalls/types/stream/direction.py,sha256=gd10wUmpfsqx87kCAPZt6u8pFiPb09WZfHKcMWAZokU,394
91
95
  pytgcalls/types/stream/external_media.py,sha256=RiuSX5tZGdNsQZ8LIRk5Lp4Ksv9oTvaccmInJRZYo4M,114
92
96
  pytgcalls/types/stream/frame.py,sha256=TXo5HZVHbbaVNBqulMhTqGODXH3bpBVlN_of1rosNUQ,586
93
- pytgcalls/types/stream/media_stream.py,sha256=sFlP8BMmCjhYp_A8U7XkGV4jbxR7PYsmJiII5Fxs0_A,11971
94
- pytgcalls/types/stream/record_stream.py,sha256=dE8nLrQaHu-L7G6FbTTwJgMIUy5qwycOwgeYO3ta28A,3035
97
+ pytgcalls/types/stream/media_stream.py,sha256=jNWNJppX2ji4CrHdkF2b6FFLeiS9KKjVDGvcWPmPrvc,11917
98
+ pytgcalls/types/stream/record_stream.py,sha256=VEKB2rSnb4U1EyoPdNteWxJ65feV58EGu5QmDEJiB2E,3037
95
99
  pytgcalls/types/stream/stream_ended.py,sha256=xR_kZwFf03hA6rw_nvI7Be7GwoCKzQf_1MKaGpPDXqY,716
96
100
  pytgcalls/types/stream/stream_frames.py,sha256=028ZhNV-mN3BGqMlmxusAV1xDQpXRYCeM0WXBZhRUhA,446
97
101
  pytgcalls/types/stream/video_quality.py,sha256=HBfWq005kh-D19MaVE9VzVdnODzrXf4IJUimCfslfiU,231
98
- py_tgcalls-2.1.0rc2.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
99
- py_tgcalls-2.1.0rc2.dist-info/METADATA,sha256=va30-VM4T6aYlTTXnFFrN83jLGFRgo3CBfoefeKXZ6o,14398
100
- py_tgcalls-2.1.0rc2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
101
- py_tgcalls-2.1.0rc2.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
102
- py_tgcalls-2.1.0rc2.dist-info/RECORD,,
102
+ py_tgcalls-2.1.0rc4.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
103
+ py_tgcalls-2.1.0rc4.dist-info/METADATA,sha256=H6X5VamKCIuV6ApcU3YNwt8ADVD9FIdBKDxO7-VQA6M,14398
104
+ py_tgcalls-2.1.0rc4.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
105
+ py_tgcalls-2.1.0rc4.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
106
+ py_tgcalls-2.1.0rc4.dist-info/RECORD,,
pytgcalls/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '2.1.0.rc2'
1
+ __version__ = '2.1.0.rc4'
@@ -46,3 +46,5 @@ class LeaveCall(Scaffold):
46
46
  return
47
47
  if chat_id < 0: # type: ignore
48
48
  self._need_unmute.discard(chat_id)
49
+ self._presentations.discard(chat_id)
50
+ self._call_sources.pop(chat_id, None)
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import logging
3
+ from pathlib import Path
3
4
  from typing import Optional
4
5
  from typing import Union
5
6
 
@@ -12,7 +13,7 @@ from ntgcalls import TransportParseException
12
13
  from ...exceptions import NoActiveGroupCall
13
14
  from ...exceptions import TimedOutAnswer
14
15
  from ...exceptions import UnMuteNeeded
15
- from ...mtproto import BridgedClient
16
+ from ...media_devices.input_device import InputDevice
16
17
  from ...mtproto_required import mtproto_required
17
18
  from ...mutex import mutex
18
19
  from ...scaffold import Scaffold
@@ -34,14 +35,9 @@ class Play(Scaffold):
34
35
  async def play(
35
36
  self,
36
37
  chat_id: Union[int, str],
37
- stream: Optional[Stream] = None,
38
+ stream: Optional[Union[str, Path, InputDevice, Stream]] = None,
38
39
  config: Optional[Union[CallConfig, GroupCallConfig]] = None,
39
40
  ):
40
- def log_retries(r: int):
41
- (py_logger.warning if r >= 1 else py_logger.info)(
42
- f'Telegram is having some internal server issues. '
43
- f'Retrying {r + 1} of 3',
44
- )
45
41
  chat_id = await self.resolve_chat_id(chat_id)
46
42
  is_p2p = chat_id > 0 # type: ignore
47
43
  if config is None:
@@ -168,7 +164,7 @@ class Play(Scaffold):
168
164
  except TelegramServerError:
169
165
  if retries == 3 or is_p2p:
170
166
  raise
171
- log_retries(retries)
167
+ self._log_retries(retries)
172
168
  except Exception:
173
169
  try:
174
170
  await self._binding.stop(chat_id)
@@ -179,64 +175,11 @@ class Play(Scaffold):
179
175
  self._wait_connect.pop(chat_id, None)
180
176
 
181
177
  if isinstance(config, GroupCallConfig):
182
- if media_description.screen is not None:
183
- for retries in range(4):
184
- try:
185
- self._wait_connect[
186
- chat_id
187
- ] = self.loop.create_future()
188
- payload = await self._binding.init_presentation(
189
- chat_id,
190
- )
191
- result_params = await self._app.join_presentation(
192
- chat_id,
193
- payload,
194
- )
195
- await self._binding.connect(
196
- chat_id,
197
- result_params,
198
- True,
199
- )
200
- await self._wait_connect[chat_id]
201
- self._presentations.add(chat_id)
202
- except TelegramServerError:
203
- if retries == 3:
204
- raise
205
- log_retries(retries)
206
- finally:
207
- self._wait_connect.pop(chat_id, None)
208
- elif chat_id in self._presentations:
209
- await self._binding.stop_presentation(chat_id)
210
- await self._app.leave_presentation(chat_id)
211
- self._presentations.discard(chat_id)
212
-
213
- if isinstance(config, GroupCallConfig):
214
- participants = await self._app.get_group_call_participants(
178
+ await self._join_presentation(
215
179
  chat_id,
180
+ media_description.screen is not None,
216
181
  )
217
- for x in participants:
218
- if x.video_info is not None:
219
- self._videos_id[
220
- chat_id
221
- ] = x.video_info.endpoint
222
- self._binding.add_incoming_video(
223
- chat_id,
224
- x.video_info.endpoint,
225
- x.video_info.sources,
226
- )
227
- if x.presentation_info is not None:
228
- self._presentations_id[
229
- chat_id
230
- ] = x.presentation_info.endpoint
231
- self._binding.add_incoming_video(
232
- chat_id,
233
- x.presentation_info.endpoint,
234
- x.presentation_info.sources,
235
- )
236
- if x.user_id == BridgedClient.chat_id(
237
- self._cache_local_peer,
238
- ) and x.muted_by_admin:
239
- self._need_unmute.add(chat_id)
182
+ await self._update_sources(chat_id)
240
183
  except FileError as e:
241
184
  raise FileNotFoundError(e)
242
185
  except TransportParseException:
@@ -32,10 +32,16 @@ class Record(Scaffold):
32
32
  if chat_id not in await self._binding.calls():
33
33
  await self.play(chat_id, config=config)
34
34
  try:
35
- return await self._binding.set_stream_sources(
35
+ await self._binding.set_stream_sources(
36
36
  chat_id,
37
37
  StreamMode.PLAYBACK,
38
38
  media_description,
39
39
  )
40
+ if isinstance(chat_id, int) and chat_id < 0:
41
+ await self._join_presentation(
42
+ chat_id,
43
+ media_description.screen is not None,
44
+ )
45
+ await self._update_sources(chat_id)
40
46
  except FileError as e:
41
47
  raise FileNotFoundError(e)
@@ -1,19 +1,25 @@
1
1
  from .cache_peer import CachePeer
2
2
  from .call_holder import CallHolder
3
3
  from .cpu_usage import CpuUsage
4
+ from .join_presentation import JoinPresentation
5
+ from .log_retries import LogRetries
4
6
  from .ping import Ping
5
7
  from .resolve_chat_id import ResolveChatID
6
8
  from .run import Run
7
9
  from .start import Start
10
+ from .update_sources import UpdateSources
8
11
 
9
12
 
10
13
  class Utilities(
11
14
  CachePeer,
12
15
  CallHolder,
13
16
  CpuUsage,
17
+ JoinPresentation,
18
+ LogRetries,
14
19
  Ping,
15
20
  ResolveChatID,
16
21
  Run,
17
22
  Start,
23
+ UpdateSources,
18
24
  ):
19
25
  pass
@@ -0,0 +1,46 @@
1
+ from typing import Union
2
+
3
+ from ntgcalls import TelegramServerError
4
+
5
+ from ...scaffold import Scaffold
6
+
7
+
8
+ class JoinPresentation(Scaffold):
9
+ async def _join_presentation(
10
+ self,
11
+ chat_id: Union[int, str],
12
+ join: bool,
13
+ ):
14
+ if join:
15
+ if chat_id in self._presentations:
16
+ return
17
+ for retries in range(4):
18
+ try:
19
+ self._wait_connect[
20
+ chat_id
21
+ ] = self.loop.create_future()
22
+ payload = await self._binding.init_presentation(
23
+ chat_id,
24
+ )
25
+ result_params = await self._app.join_presentation(
26
+ chat_id,
27
+ payload,
28
+ )
29
+ await self._binding.connect(
30
+ chat_id,
31
+ result_params,
32
+ True,
33
+ )
34
+ await self._wait_connect[chat_id]
35
+ self._presentations.add(chat_id)
36
+ break
37
+ except TelegramServerError:
38
+ if retries == 3:
39
+ raise
40
+ self._log_retries(retries)
41
+ finally:
42
+ self._wait_connect.pop(chat_id, None)
43
+ elif chat_id in self._call_sources:
44
+ await self._binding.stop_presentation(chat_id)
45
+ await self._app.leave_presentation(chat_id)
46
+ self._presentations.discard(chat_id)
@@ -0,0 +1,14 @@
1
+ import logging
2
+
3
+ from ...scaffold import Scaffold
4
+
5
+ py_logger = logging.getLogger('pytgcalls')
6
+
7
+
8
+ class LogRetries(Scaffold):
9
+ @staticmethod
10
+ def _log_retries(r: int):
11
+ (py_logger.warning if r >= 1 else py_logger.info)(
12
+ f'Telegram is having some internal server issues. '
13
+ f'Retrying {r + 1} of 3',
14
+ )
@@ -94,54 +94,66 @@ class Start(Scaffold):
94
94
  action = participant.action
95
95
  chat_peer = self._cache_user_peer.get(chat_id)
96
96
  user_id = participant.user_id
97
- was_camera = user_id in self._videos_id
98
- was_screen = user_id in self._presentations_id
97
+ if chat_id in self._call_sources:
98
+ call_sources = self._call_sources[chat_id]
99
+ was_camera = user_id in call_sources.camera
100
+ was_screen = user_id in call_sources.presentation
99
101
 
100
- if was_camera != participant.video_camera:
101
- if participant.video_info:
102
- self._videos_id[
103
- user_id
104
- ] = participant.video_info.endpoint
105
- try:
106
- await self._binding.add_incoming_video(
107
- chat_id,
108
- participant.video_info.endpoint,
109
- participant.video_info.sources,
110
- )
111
- except ConnectionNotFound:
112
- pass
113
- elif user_id in self._videos_id:
114
- try:
115
- await self._binding.remove_incoming_video(
116
- chat_id,
117
- self._videos_id[user_id],
102
+ if was_camera != participant.video_camera:
103
+ if participant.video_info:
104
+ self._call_sources[chat_id].camera[
105
+ user_id
106
+ ] = participant.video_info.endpoint
107
+ try:
108
+ await self._binding.add_incoming_video(
109
+ chat_id,
110
+ participant.video_info.endpoint,
111
+ participant.video_info.sources,
112
+ )
113
+ except (ConnectionNotFound, ConnectionError):
114
+ pass
115
+ elif user_id in self._call_sources[chat_id].camera:
116
+ try:
117
+ await self._binding.remove_incoming_video(
118
+ chat_id,
119
+ self._call_sources[
120
+ chat_id
121
+ ].camera[user_id],
122
+ )
123
+ except (ConnectionNotFound, ConnectionError):
124
+ pass
125
+ self._call_sources[chat_id].camera.pop(
126
+ user_id, None,
118
127
  )
119
- except ConnectionNotFound:
120
- pass
121
- self._videos_id.pop(user_id, None)
122
128
 
123
- if was_screen != participant.screen_sharing:
124
- if participant.presentation_info:
125
- self._presentations_id[
126
- user_id
127
- ] = participant.presentation_info.endpoint
128
- try:
129
- await self._binding.add_incoming_video(
130
- chat_id,
131
- participant.presentation_info.endpoint,
132
- participant.presentation_info.sources,
133
- )
134
- except ConnectionNotFound:
135
- pass
136
- elif user_id in self._presentations_id:
137
- try:
138
- await self._binding.remove_incoming_video(
139
- chat_id,
140
- self._presentations_id[user_id],
129
+ if was_screen != participant.screen_sharing:
130
+ if participant.presentation_info:
131
+ self._call_sources[chat_id].presentation[
132
+ user_id
133
+ ] = participant.presentation_info.endpoint
134
+ try:
135
+ await self._binding.add_incoming_video(
136
+ chat_id,
137
+ participant.presentation_info.endpoint,
138
+ participant.presentation_info.sources,
139
+ )
140
+ except (ConnectionNotFound, ConnectionError):
141
+ pass
142
+ elif user_id in self._call_sources[
143
+ chat_id
144
+ ].presentation:
145
+ try:
146
+ await self._binding.remove_incoming_video(
147
+ chat_id,
148
+ self._call_sources[
149
+ chat_id
150
+ ].presentation[user_id],
151
+ )
152
+ except (ConnectionNotFound, ConnectionError):
153
+ pass
154
+ self._call_sources[chat_id].presentation.pop(
155
+ user_id, None,
141
156
  )
142
- except ConnectionNotFound:
143
- pass
144
- self._presentations_id.pop(user_id, None)
145
157
 
146
158
  if chat_peer:
147
159
  is_self = BridgedClient.chat_id(
@@ -1,3 +1,4 @@
1
+ from pathlib import Path
1
2
  from typing import Optional
2
3
  from typing import Union
3
4
 
@@ -5,6 +6,7 @@ from ntgcalls import AudioDescription
5
6
  from ntgcalls import MediaDescription
6
7
  from ntgcalls import VideoDescription
7
8
 
9
+ from ...media_devices.input_device import InputDevice
8
10
  from ...types import RecordStream
9
11
  from ...types.raw import AudioStream
10
12
  from ...types.raw import Stream
@@ -15,9 +17,11 @@ from ...types.stream.media_stream import MediaStream
15
17
  class StreamParams:
16
18
  @staticmethod
17
19
  async def get_stream_params(
18
- stream: Optional[Stream],
20
+ stream: Optional[Union[str, Path, InputDevice, Stream]],
19
21
  ) -> MediaDescription:
20
22
  if stream is not None:
23
+ if isinstance(stream, (str, Path, InputDevice)):
24
+ stream = MediaStream(stream)
21
25
  if isinstance(stream, MediaStream):
22
26
  await stream.check_stream()
23
27
  elif isinstance(stream, RecordStream):
@@ -52,7 +56,7 @@ class StreamParams:
52
56
 
53
57
  @staticmethod
54
58
  def _parse_stream_description(
55
- stream: Stream,
59
+ stream: Optional[Stream],
56
60
  ) -> MediaDescription:
57
61
  return MediaDescription(
58
62
  microphone=StreamParams._parse_media_description(
@@ -0,0 +1,42 @@
1
+ from typing import Union
2
+
3
+ from ...mtproto import BridgedClient
4
+ from ...scaffold import Scaffold
5
+ from ...types.calls import CallSources
6
+
7
+
8
+ class UpdateSources(Scaffold):
9
+ async def _update_sources(
10
+ self,
11
+ chat_id: Union[int, str],
12
+ ):
13
+ participants = await self._app.get_group_call_participants(
14
+ chat_id,
15
+ )
16
+ if chat_id not in self._call_sources:
17
+ self._call_sources[chat_id] = CallSources()
18
+ for x in participants:
19
+ if x.video_info is not None and \
20
+ x.user_id not in self._call_sources[chat_id].camera:
21
+ self._call_sources[chat_id].camera[
22
+ x.user_id
23
+ ] = x.video_info.endpoint
24
+ await self._binding.add_incoming_video(
25
+ chat_id,
26
+ x.video_info.endpoint,
27
+ x.video_info.sources,
28
+ )
29
+ if x.presentation_info is not None and \
30
+ x.user_id not in self._call_sources[chat_id].presentation:
31
+ self._call_sources[chat_id].presentation[
32
+ x.user_id
33
+ ] = x.presentation_info.endpoint
34
+ await self._binding.add_incoming_video(
35
+ chat_id,
36
+ x.presentation_info.endpoint,
37
+ x.presentation_info.sources,
38
+ )
39
+ if x.user_id == BridgedClient.chat_id(
40
+ self._cache_local_peer,
41
+ ) and x.muted_by_admin:
42
+ self._need_unmute.add(chat_id)
pytgcalls/scaffold.py CHANGED
@@ -22,8 +22,7 @@ class Scaffold(HandlersHolder):
22
22
  self.loop = None
23
23
  self._need_unmute = set()
24
24
  self._p2p_configs = dict()
25
- self._videos_id = dict()
26
- self._presentations_id = dict()
25
+ self._call_sources = dict()
27
26
  self._wait_connect = dict()
28
27
  self._presentations = set()
29
28
 
@@ -42,5 +41,15 @@ class Scaffold(HandlersHolder):
42
41
  async def play(self, chat_id: Union[int, str], stream=None, config=None):
43
42
  pass
44
43
 
44
+ async def _update_sources(self, chat_id: Union[int, str]):
45
+ pass
46
+
47
+ async def _join_presentation(self, chat_id: Union[int, str], join: bool):
48
+ pass
49
+
50
+ @staticmethod
51
+ def _log_retries(r: int):
52
+ pass
53
+
45
54
  def on_update(self, filters=None):
46
55
  pass
@@ -2,6 +2,7 @@ from .call import Call
2
2
  from .call_config import CallConfig
3
3
  from .call_data import CallData
4
4
  from .call_protocol import CallProtocol
5
+ from .call_sources import CallSources
5
6
  from .group_call_config import GroupCallConfig
6
7
  from .raw_call_update import RawCallUpdate
7
8
 
@@ -10,6 +11,7 @@ __all__ = (
10
11
  'CallData',
11
12
  'CallConfig',
12
13
  'CallProtocol',
14
+ 'CallSources',
13
15
  'GroupCallConfig',
14
16
  'RawCallUpdate',
15
17
  )
@@ -0,0 +1,4 @@
1
+ class CallSources:
2
+ def __init__(self):
3
+ self.camera = dict()
4
+ self.presentation = dict()
@@ -91,7 +91,6 @@ class MediaStream(Stream):
91
91
  if media_path & ExternalMedia.VIDEO:
92
92
  self._is_video_external = True
93
93
  elif isinstance(media_path, (InputDevice, ScreenDevice)):
94
- print('MediaStream', media_path.is_video)
95
94
  if media_path.is_video:
96
95
  self._media_path = media_path.metadata
97
96
  self._is_media_device = True
@@ -86,8 +86,8 @@ class RecordStream(Stream):
86
86
  media_source=MediaSource.EXTERNAL,
87
87
  path='',
88
88
  parameters=VideoParameters(
89
- width=0,
90
- height=0,
89
+ width=-1,
90
+ height=-1,
91
91
  frame_rate=0,
92
92
  ),
93
93
  )