py-tgcalls 2.0.6__py3-none-any.whl → 2.1.0__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 (65) hide show
  1. {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dist-info}/METADATA +11 -16
  2. py_tgcalls-2.1.0.dist-info/RECORD +106 -0
  3. {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dist-info}/WHEEL +1 -1
  4. pytgcalls/__init__.py +2 -0
  5. pytgcalls/__version__.py +1 -1
  6. pytgcalls/exceptions.py +7 -24
  7. pytgcalls/ffmpeg.py +2 -2
  8. pytgcalls/filters.py +49 -6
  9. pytgcalls/handlers/handlers_holder.py +1 -1
  10. pytgcalls/media_devices/__init__.py +6 -2
  11. pytgcalls/media_devices/device_info.py +8 -15
  12. pytgcalls/media_devices/input_device.py +11 -0
  13. pytgcalls/media_devices/media_devices.py +41 -92
  14. pytgcalls/media_devices/screen_device.py +10 -0
  15. pytgcalls/media_devices/speaker_device.py +10 -0
  16. pytgcalls/methods/calls/change_volume_call.py +3 -0
  17. pytgcalls/methods/calls/get_participants.py +5 -1
  18. pytgcalls/methods/calls/leave_call.py +3 -1
  19. pytgcalls/methods/stream/__init__.py +14 -10
  20. pytgcalls/methods/stream/{mute_stream.py → mute.py} +2 -2
  21. pytgcalls/methods/stream/{pause_stream.py → pause.py} +2 -2
  22. pytgcalls/methods/stream/play.py +27 -21
  23. pytgcalls/methods/stream/record.py +49 -0
  24. pytgcalls/methods/stream/{resume_stream.py → resume.py} +2 -2
  25. pytgcalls/methods/stream/send_frame.py +38 -0
  26. pytgcalls/methods/stream/{played_time.py → time.py} +5 -3
  27. pytgcalls/methods/stream/{unmute_stream.py → unmute.py} +2 -2
  28. pytgcalls/methods/utilities/__init__.py +6 -0
  29. pytgcalls/methods/utilities/call_holder.py +5 -2
  30. pytgcalls/methods/utilities/join_presentation.py +50 -0
  31. pytgcalls/methods/utilities/log_retries.py +14 -0
  32. pytgcalls/methods/utilities/start.py +136 -16
  33. pytgcalls/methods/utilities/stream_params.py +69 -22
  34. pytgcalls/methods/utilities/update_sources.py +42 -0
  35. pytgcalls/mtproto/bridged_client.py +36 -2
  36. pytgcalls/mtproto/client_cache.py +46 -16
  37. pytgcalls/mtproto/hydrogram_client.py +72 -23
  38. pytgcalls/mtproto/mtproto_client.py +32 -4
  39. pytgcalls/mtproto/pyrogram_client.py +72 -23
  40. pytgcalls/mtproto/telethon_client.py +97 -33
  41. pytgcalls/scaffold.py +15 -0
  42. pytgcalls/types/__init__.py +14 -4
  43. pytgcalls/types/calls/__init__.py +2 -0
  44. pytgcalls/types/calls/call.py +5 -3
  45. pytgcalls/types/calls/call_sources.py +4 -0
  46. pytgcalls/types/chats/group_call_participant.py +23 -0
  47. pytgcalls/types/py_object.py +9 -10
  48. pytgcalls/types/raw/audio_stream.py +3 -3
  49. pytgcalls/types/raw/stream.py +8 -4
  50. pytgcalls/types/raw/video_stream.py +5 -4
  51. pytgcalls/types/stream/__init__.py +14 -4
  52. pytgcalls/types/stream/device.py +36 -0
  53. pytgcalls/types/stream/direction.py +25 -0
  54. pytgcalls/types/stream/external_media.py +8 -0
  55. pytgcalls/types/stream/frame.py +26 -0
  56. pytgcalls/types/stream/media_stream.py +178 -107
  57. pytgcalls/types/stream/record_stream.py +99 -0
  58. pytgcalls/types/stream/stream_ended.py +32 -0
  59. pytgcalls/types/stream/stream_frames.py +20 -0
  60. py_tgcalls-2.0.6.dist-info/RECORD +0 -93
  61. pytgcalls/media_devices/screen_info.py +0 -45
  62. pytgcalls/types/stream/stream_audio_ended.py +0 -9
  63. pytgcalls/types/stream/stream_video_ended.py +0 -9
  64. {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dist-info}/LICENSE +0 -0
  65. {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: py-tgcalls
3
- Version: 2.0.6
3
+ Version: 2.1.0
4
4
  Summary: Async client API for the Telegram Calls.
5
5
  Author-email: Laky-64 <iraci.matteo@gmail.com>
6
- License: GNU LESSER GENERAL PUBLIC LICENSE
6
+ License: GNU LESSER GENERAL PUBLIC LICENSE
7
7
  Version 3, 29 June 2007
8
8
 
9
9
  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
@@ -178,25 +178,23 @@ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGP
178
178
  Classifier: Operating System :: OS Independent
179
179
  Classifier: Programming Language :: Python :: 3
180
180
  Classifier: Programming Language :: Python :: 3 :: Only
181
- Classifier: Programming Language :: Python :: 3.8
182
181
  Classifier: Programming Language :: Python :: 3.9
183
182
  Classifier: Programming Language :: Python :: 3.10
184
183
  Classifier: Programming Language :: Python :: 3.11
185
184
  Classifier: Programming Language :: Python :: 3.12
186
- Requires-Python: >=3.8
185
+ Classifier: Programming Language :: Python :: 3.13
186
+ 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.3.0,>=1.2.0
191
- Requires-Dist: psutil
192
- Requires-Dist: screeninfo
190
+ Requires-Dist: ntgcalls<1.4.0,>=1.3.0
193
191
  Requires-Dist: deprecation
194
- Provides-Extra: hydrogram
195
- Requires-Dist: hydrogram>=0.1.4; extra == "hydrogram"
196
192
  Provides-Extra: pyrogram
197
193
  Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
198
194
  Provides-Extra: telethon
199
195
  Requires-Dist: telethon>=1.24.0; extra == "telethon"
196
+ Provides-Extra: hydrogram
197
+ Requires-Dist: hydrogram>=0.1.4; extra == "hydrogram"
200
198
 
201
199
  <img src="https://raw.githubusercontent.com/pytgcalls/pytgcalls/master/.github/images/banner.png" alt="pytgcalls logo" />
202
200
  <p align="center">
@@ -223,23 +221,20 @@ Requires-Dist: telethon>=1.24.0; extra == "telethon"
223
221
  </a>
224
222
  </p>
225
223
 
226
- # PyTgCalls [![PyPI](https://img.shields.io/pypi/v/py-tgcalls.svg?logo=python&logoColor=%23959DA5&label=pypi&labelColor=%23282f37)](https://pypi.org/project/py-tgcalls/) [![Downloads](https://pepy.tech/badge/py-tgcalls)](https://pepy.tech/project/py-tgcalls)
224
+ # PyTgCalls [![PyPI](https://img.shields.io/pypi/v/py-tgcalls.svg?logo=python&logoColor=%23959DA5&label=pypi&labelColor=%23282f37)](https://pypi.org/project/py-tgcalls/) [![Downloads](https://img.shields.io/pepy/dt/py-tgcalls?logoColor=%23959DA5&labelColor=%23282f37&color=%2328A745)](https://pepy.tech/project/py-tgcalls)
227
225
  This project allows making Telegram call using MtProto and WebRTC, this is possible thanks to the power of [NTgCalls] library and [@evgeny-nadymov]
228
226
 
229
227
  #### Example Usage
230
228
  ```python
231
229
  from pytgcalls import PyTgCalls
232
230
  from pytgcalls import idle
233
- from pytgcalls.types import MediaStream
234
231
  ...
235
232
  chat_id = -1001185324811
236
233
  app = PyTgCalls(client)
237
234
  app.start()
238
235
  app.play(
239
236
  chat_id,
240
- MediaStream(
241
- 'http://docs.evostream.com/sample_content/assets/sintel1m720p.mp4',
242
- )
237
+ 'http://docs.evostream.com/sample_content/assets/sintel1m720p.mp4',
243
238
  )
244
239
  idle()
245
240
  ```
@@ -252,7 +247,7 @@ idle()
252
247
  - Mute/unmute, pause/resume, stop/play, volume control and more...
253
248
 
254
249
  ## Requirements
255
- - Python 3.8 or higher.
250
+ - Python 3.9 or higher.
256
251
  - An MTProto Client
257
252
  - A [Telegram API key](https://docs.pyrogram.org/intro/setup#api-keys).
258
253
 
@@ -0,0 +1,106 @@
1
+ pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
2
+ pytgcalls/__version__.py,sha256=LbU43-7hsLmdXWI0wTAl3y6D3Tr7CbJiDOLXwl7clj8,22
3
+ pytgcalls/environment.py,sha256=ctCHACvG6l8SdpPewSBhOvc70kbwpv18maC0TwLvZ08,1924
4
+ pytgcalls/exceptions.py,sha256=-xy-V-qIDx4fvAoeJfSWA0rjqKSgBN6kDr2No7ZuULo,3778
5
+ pytgcalls/ffmpeg.py,sha256=tm6DBxyNfPh3h3an-b2s9x1UyX-cvkCdov9prlXxVZY,8649
6
+ pytgcalls/filters.py,sha256=qTFDlt-23xnMh_Ug2WmmOUf13JPX6_yacrv7c3F9Pp0,6125
7
+ pytgcalls/mtproto_required.py,sha256=6B-31p5qH_6oekUgypV4nK3hqPS6Nr-pA8S81wjnbaY,630
8
+ pytgcalls/mutex.py,sha256=Frjji5Ctzlk4AXEBuBLnDK-7HbtreoV6zuyKpFpMNI4,236
9
+ pytgcalls/pytgcalls.py,sha256=oBcWgBwusnXmjHrLEE99VVXARReVyrXdn9SyeBWHbVo,1479
10
+ pytgcalls/pytgcalls_session.py,sha256=_BGJWvf7t3mki2DhlEPjh9cypvYuSFkMSxzTsfepwUk,2719
11
+ pytgcalls/scaffold.py,sha256=SwePuT9V8LdOFy0KRlAnRSbsB2zeGQ4fs9ilrq7HZYI,1353
12
+ pytgcalls/statictypes.py,sha256=CdlqgQNhTZ_uTE8-B8m01fJ7TlD2B42EI2QBPxDdAtA,3842
13
+ pytgcalls/sync.py,sha256=IsOH3TD7cxUg_-zdGt12HoS8sBlXvcGayPZAoxxKM48,3396
14
+ pytgcalls/version_manager.py,sha256=egeGgvb66zWlLTMuw2U-b0x8MfnRzMm1xAEVN87HF5c,296
15
+ pytgcalls/ytdlp.py,sha256=jRA-mKQEXleWvaoGv9wtMa77aRjxsyQxn6_0tXI3-sA,2435
16
+ pytgcalls/custom_api/__init__.py,sha256=ZT8d0lc2YrDuw_YSFAXXHHMewoXGFZ-ANOBIAr0vGFQ,60
17
+ pytgcalls/custom_api/custom_api.py,sha256=Ko3aS6psrwPmOhRPxvG0fepXt4STrA0StvINSxz4Cj8,1890
18
+ pytgcalls/handlers/__init__.py,sha256=pubbxI4pLqQpAKf8-toD6ija4cpSvbCJOQFjTiDjX1E,75
19
+ pytgcalls/handlers/handlers_holder.py,sha256=Rxy6MNdiGOOO5thxlQQyvuXmcbdCTe0N_Q4H6YzpjHU,1177
20
+ pytgcalls/media_devices/__init__.py,sha256=QC7OccjY-YkcJ1xF9d1VRfM9hdgExWbInMYW92YBJ_s,309
21
+ pytgcalls/media_devices/device_info.py,sha256=AleyVRXvZ0ylr03JQq8gPm5L-f5ySDwokDtW_Czcg50,266
22
+ pytgcalls/media_devices/input_device.py,sha256=-Z5hjhdc1Y68mZQpVW-QgMePKGe_c5zJcYJHIzYElOY,225
23
+ pytgcalls/media_devices/media_devices.py,sha256=ZgRF8j4jCJNWrW1HCUUWZMcZ9V7LOcvm_jJmy8g7ieQ,1333
24
+ pytgcalls/media_devices/screen_device.py,sha256=B1MCpcqW-QXKcVafNdPQfd2Es6-ejkX2NdwbfuYmhv4,201
25
+ pytgcalls/media_devices/speaker_device.py,sha256=39aAE2oEvlhRiUwHlIJCrSUPWIy-8J9_oq1TmCq5IjA,200
26
+ pytgcalls/methods/__init__.py,sha256=hk1blAT5u_Isemdrg0nqInLsdRzTTZnak5NdAfkBPAk,217
27
+ pytgcalls/methods/calls/__init__.py,sha256=xg4DZZClEnxwaj-DAq3e8gSR-g-MiYBdUEBth64lSXA,214
28
+ pytgcalls/methods/calls/change_volume_call.py,sha256=xMUszg44Gs1RgTXGCwcWEESnwu3XVkW8Kx9HGLDGSEo,842
29
+ pytgcalls/methods/calls/get_participants.py,sha256=HDEMwZwNZM7KSb76P5XVH46qNONvBEVg4x_e-rgJscI,716
30
+ pytgcalls/methods/calls/leave_call.py,sha256=ay07za5mNKmXGzmpeU6gJJmHVrLeXRtRmFT4QP94pGA,1475
31
+ pytgcalls/methods/decorators/__init__.py,sha256=TCGaEVZnHjtOwv-3PNfaCVm0kyFhJApUPUNntt6MwyM,78
32
+ pytgcalls/methods/decorators/on_update.py,sha256=ZTL4YcQk0N4Ru56a5WItUvkSN5SAqr6_RDZvXmZMIHs,316
33
+ pytgcalls/methods/stream/__init__.py,sha256=mAcOih0-NT6T_Gspej6mySpJNPuEe46sUwgKV3vSvYM,336
34
+ pytgcalls/methods/stream/mute.py,sha256=ZrZS_EeNUeUxb6UgbdhwXUdRX826u-qSjH5a6sg7LsE,557
35
+ pytgcalls/methods/stream/pause.py,sha256=-kNvWQuv5VlssNcL-M6rkT5TKFmXlbOzJrDny95qsUc,560
36
+ pytgcalls/methods/stream/play.py,sha256=-Ld5TQWLhU_ktKrb7q1Xrym36UGrWhaenIbjj-3mM3k,7524
37
+ pytgcalls/methods/stream/record.py,sha256=p9td2tZ4-QMpY8I8LDc1PrFVN3g-vlDINF5I7CmLsvA,1567
38
+ pytgcalls/methods/stream/resume.py,sha256=AUHU3AtpXO2rtp2V1EKSC_KAWTk2KHMiHaqHluYy31M,563
39
+ pytgcalls/methods/stream/send_frame.py,sha256=Kj9R8OqUM-g7pt-FiWP-US7sCFkH5ciPr9S8v-WPtLg,1062
40
+ pytgcalls/methods/stream/time.py,sha256=65Hc5pQaN6z6nxgwT9Zxgsnl_UosvMkcICqE2hisNWA,659
41
+ pytgcalls/methods/stream/unmute.py,sha256=wgWpxakIPLzZCgojC-cIkDSnF9LtDZwdTsBOctdHibQ,563
42
+ pytgcalls/methods/utilities/__init__.py,sha256=HHAkTQEX_23uwo1fxnCIoE2rLBm7fKSQa52SwBLudTI,522
43
+ pytgcalls/methods/utilities/cache_peer.py,sha256=Ylt0wCCJOoNKf1wZEXjfE8aBZKUIIgdRUFOMTGA5DfE,140
44
+ pytgcalls/methods/utilities/call_holder.py,sha256=MhIbwCG6DROd9_bHGa6aqu-rB0y4sngzPBj82zLtAXU,1068
45
+ pytgcalls/methods/utilities/compose.py,sha256=Nzdv8orMmka5NIBZ1SW1nsqXRzArZl4m6FdZU7syaR4,334
46
+ pytgcalls/methods/utilities/cpu_usage.py,sha256=Mbga4MFCIwuh7WC8sqBbv1Pa6ALcp5AIDyfYMH_Bix4,162
47
+ pytgcalls/methods/utilities/idle.py,sha256=MDdzHTv1ws2yBhsvhBUnssGdghkZ2KwR0HUCPOwV38o,814
48
+ pytgcalls/methods/utilities/join_presentation.py,sha256=gvxmFVzf6xsQZ4bPOz5wK8oZ0up1QD0DB2HpO5JcpGw,1671
49
+ pytgcalls/methods/utilities/log_retries.py,sha256=6nD9J3350t82I0PKzK1pVx3ZaCBHATReiXYMs3PuVhQ,342
50
+ pytgcalls/methods/utilities/ping.py,sha256=hhIMSHk2BzMB-IKpwLdZFVrsEvGm2ftJwKLs1k4anh8,244
51
+ pytgcalls/methods/utilities/resolve_chat_id.py,sha256=92x2LHbUlnJMm-kS3fXOYmzYpY2TZbqtQD2rw3eBXDY,382
52
+ pytgcalls/methods/utilities/run.py,sha256=cnYQd2xB5Cr_WS0Q2cXJZPGiN6JOCULzj1r4xXVyrlg,152
53
+ pytgcalls/methods/utilities/start.py,sha256=Qc-uP9B6fgHtNOfjKJfgtYnWTY8JeIb6PSLmWPFXLOk,14376
54
+ pytgcalls/methods/utilities/stream_params.py,sha256=fOSloo1A7WTxaZEtOiPXjdexMeoJjq8CZtfHuIFX7Ns,3325
55
+ pytgcalls/methods/utilities/update_sources.py,sha256=ISF6u3rk4IcVrPOEOB-uZUDfnwUp2_y1_2g9GboXpWM,1562
56
+ pytgcalls/mtproto/__init__.py,sha256=X4zvzFG7km7qHyE0fdvA550WcOVO_xl_p__gvIfDGmw,130
57
+ pytgcalls/mtproto/bridged_client.py,sha256=hktnfpfBK7PEL2n7Y0kBZzI7dkLlsdrDwkYliZki8zQ,5915
58
+ pytgcalls/mtproto/client_cache.py,sha256=Mt0827e_T8DXJHOTkXhkIQUT9EUBWjoLcFcXP1gBnZY,5973
59
+ pytgcalls/mtproto/hydrogram_client.py,sha256=SbyLIffnexzI6xuSkpqzChT70xGXwti_c5sjKUZFqwQ,23031
60
+ pytgcalls/mtproto/mtproto_client.py,sha256=1C1Cc1GOrKom-70NqUOICKxjfjgpPZBSdKdwFUjZzBc,7616
61
+ pytgcalls/mtproto/pyrogram_client.py,sha256=GFt_2QoSEuRaIkUziZOnpBj8zEPiDGs55IriHU0jXwI,23213
62
+ pytgcalls/mtproto/telethon_client.py,sha256=L91_Zxud-pvtJy36fBi6daowNV2qPZHUEp5dcsu031I,22451
63
+ pytgcalls/types/__init__.py,sha256=iXAzXG5WgbICLQV1JT_F4QtOqKWC8X3334-MOv2SXW4,1127
64
+ pytgcalls/types/browsers.py,sha256=47Kr5q96n4Q4WvVhA6IUlS2egEcA9GRLlDeFcQYyc9M,9545
65
+ pytgcalls/types/cache.py,sha256=FfsOcmYnsBGPlJoTPIXXYcUSpGE3rhx6cjIH77hyUL0,1059
66
+ pytgcalls/types/dict.py,sha256=lAo9hu4VlVJa9S7P8Y81BYmKtvz0rH7hwpGcH3ynHUw,78
67
+ pytgcalls/types/flag.py,sha256=dQPcQmTgTQzcOLTvGe8t_e9mY4qsVnCZFrrTk17b2Xw,132
68
+ pytgcalls/types/list.py,sha256=UjP_XxxMpPkLlu6yEy29JYqOM5VITFwwJcDm0wZni1c,78
69
+ pytgcalls/types/participant_list.py,sha256=LmGjU63MK1v3SS2_4xNbk04OOjmukNdAXYLRn2L-730,916
70
+ pytgcalls/types/py_object.py,sha256=__GNXgffGK4jhdF4QPqIknd0k2myObJf_7fW3pNGpbU,836
71
+ pytgcalls/types/update.py,sha256=wPCzWLhrsScZ3ksRTyt8IuDaaG5YI-ItG_Yw-OqzK2Y,157
72
+ pytgcalls/types/user_agent.py,sha256=sSfeGqUe0v0wqBgdVszNFK0iOC_0Tdyto9CglBXlY4U,1086
73
+ pytgcalls/types/calls/__init__.py,sha256=f0zMKm_mwvNsDRPgs8IopuCEefU3V0-kuHAFsTMBoAc,403
74
+ pytgcalls/types/calls/call.py,sha256=n7LW7FRNT2qJzbYC3D312judOlVUuocQ1eW9l9scGCo,546
75
+ pytgcalls/types/calls/call_config.py,sha256=b6P43YTGF2t7E2CyD1mSYPJDUBvYYeHoxB3hSbTVyOY,120
76
+ pytgcalls/types/calls/call_data.py,sha256=-qPj2QhWv32Xs7LyFQY4hiWDqJ21B8VBvdzREK8bDvY,544
77
+ pytgcalls/types/calls/call_protocol.py,sha256=OVIQs1VgdY-DWbZbNr41hjLA4pGQvHx8Rgom1_NhJxQ,408
78
+ pytgcalls/types/calls/call_sources.py,sha256=sBhumPgEaN8uAKjBwb1Zf_Ag0qrceti2mURXqMhBusg,107
79
+ pytgcalls/types/calls/group_call_config.py,sha256=auKH-hZJWj8PhTkyeQ_VK2z9NpNvNC7Scl_IhEUMnQM,353
80
+ pytgcalls/types/calls/raw_call_update.py,sha256=hpNw6HrTW8Z36Lh2HinS-wzprryRtsIxyIFbIfjGgeI,795
81
+ pytgcalls/types/chats/__init__.py,sha256=v8pUp_vbr2kQpyHtAQc80N-YqzmXHe9SbllUsa6njkU,261
82
+ pytgcalls/types/chats/chat_update.py,sha256=wh8v2I-pZxJWGNBjIwWRbhY-ZCI4v8qGabDTpuJaHu0,619
83
+ pytgcalls/types/chats/group_call_participant.py,sha256=XG4dekFV9GYHASqy_bSE6YnZ1uDunFFif-xLAy_eYGY,1711
84
+ pytgcalls/types/chats/updated_group_call_participant.py,sha256=-KID-z-4e43fhYWQp0pNMKPfmZizbJHXyzn6yLtIGNg,291
85
+ pytgcalls/types/raw/__init__.py,sha256=ROHsKFeUMUtlFbx2rhfrdB-TuVm0zBuvNo29Ccn5614,308
86
+ pytgcalls/types/raw/audio_parameters.py,sha256=1DsBPwdn_Ukd2Tbkb3whP_ILo9xJY_3XNNmbO4_NO9Q,449
87
+ pytgcalls/types/raw/audio_stream.py,sha256=oN7Sx9oLbNFuNXiGYpoNabMwqWKGquLiHEywef28o7c,488
88
+ pytgcalls/types/raw/stream.py,sha256=xJ3w77ofKFnLIA4cAWIuw2yREpMPvTzIvtei-3xEbJQ,666
89
+ pytgcalls/types/raw/video_parameters.py,sha256=nUl9gkfYTVU0iLNGTtlZ5cZg8K6F7odIi9n8POJXCK4,639
90
+ pytgcalls/types/raw/video_stream.py,sha256=uE3jU9kJsrAoefXtDtEKNqTjcYGaQbZ1gbJ1SfiVWIc,488
91
+ pytgcalls/types/stream/__init__.py,sha256=4CfgKXoEZX8BvV_ImBlY108MvRELNCf_-CPAMJ9szgk,561
92
+ pytgcalls/types/stream/audio_quality.py,sha256=4X94ErmTeLP4TVcE3eLtPPdtluSPxgxbgTosuNJOVhc,141
93
+ pytgcalls/types/stream/device.py,sha256=EdoDg6lPE7fgoZI04Nr0E9zbIk-iRIBgYYAzVqoCBPM,961
94
+ pytgcalls/types/stream/direction.py,sha256=VepLMe-dXg4M5eVdVyIb2uxYvnpB9OJL5fEgPYUFtTI,592
95
+ pytgcalls/types/stream/external_media.py,sha256=RiuSX5tZGdNsQZ8LIRk5Lp4Ksv9oTvaccmInJRZYo4M,114
96
+ pytgcalls/types/stream/frame.py,sha256=TXo5HZVHbbaVNBqulMhTqGODXH3bpBVlN_of1rosNUQ,586
97
+ pytgcalls/types/stream/media_stream.py,sha256=_NJFTAIUdYyTh-gjiqs6aCpAIfSVjE16mBYUapMnDfg,11917
98
+ pytgcalls/types/stream/record_stream.py,sha256=RWeD9U-sqUmb_PuZQxYnExZyoibepU_8oI2yrH2Y18c,3135
99
+ pytgcalls/types/stream/stream_ended.py,sha256=xR_kZwFf03hA6rw_nvI7Be7GwoCKzQf_1MKaGpPDXqY,716
100
+ pytgcalls/types/stream/stream_frames.py,sha256=028ZhNV-mN3BGqMlmxusAV1xDQpXRYCeM0WXBZhRUhA,446
101
+ pytgcalls/types/stream/video_quality.py,sha256=HBfWq005kh-D19MaVE9VzVdnODzrXf4IJUimCfslfiU,231
102
+ py_tgcalls-2.1.0.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
103
+ py_tgcalls-2.1.0.dist-info/METADATA,sha256=WKf9Cm8Eyr7cdLT-44TOKK1ytzdlQ0E_682IQBjNGzM,14325
104
+ py_tgcalls-2.1.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
105
+ py_tgcalls-2.1.0.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
106
+ py_tgcalls-2.1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pytgcalls/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from .__version__ import __version__
2
2
  from .custom_api import CustomApi
3
+ from .media_devices import MediaDevices
3
4
  from .pytgcalls import PyTgCalls
4
5
  from .sync import compose
5
6
  from .sync import idle
@@ -9,5 +10,6 @@ __all__ = (
9
10
  'compose',
10
11
  'CustomApi',
11
12
  'PyTgCalls',
13
+ 'MediaDevices',
12
14
  'idle',
13
15
  )
pytgcalls/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '2.0.6'
1
+ __version__ = '2.1.0'
pytgcalls/exceptions.py CHANGED
@@ -37,13 +37,6 @@ class TooOldHydrogramVersion(Exception):
37
37
  )
38
38
 
39
39
 
40
- class InvalidStreamMode(Exception):
41
- def __init__(self):
42
- super().__init__(
43
- 'Invalid stream mode',
44
- )
45
-
46
-
47
40
  class NoMTProtoClientSet(Exception):
48
41
  def __init__(self):
49
42
  super().__init__(
@@ -86,13 +79,6 @@ class NotInCallError(Exception):
86
79
  )
87
80
 
88
81
 
89
- class AlreadyJoinedError(Exception):
90
- def __init__(self):
91
- super().__init__(
92
- 'Already joined into group call',
93
- )
94
-
95
-
96
82
  class ClientNotStarted(Exception):
97
83
  def __init__(self):
98
84
  super().__init__(
@@ -115,16 +101,6 @@ class TooManyCustomApiDecorators(Exception):
115
101
  )
116
102
 
117
103
 
118
- class GroupCallNotFound(Exception):
119
- def __init__(
120
- self,
121
- chat_id: int,
122
- ):
123
- super().__init__(
124
- f'Group call not found with the chat id {chat_id}',
125
- )
126
-
127
-
128
104
  class InvalidMTProtoClient(Exception):
129
105
  def __init__(self):
130
106
  super().__init__(
@@ -186,3 +162,10 @@ class MTProtoClientNotConnected(Exception):
186
162
  super().__init__(
187
163
  'MTProto client not connected',
188
164
  )
165
+
166
+
167
+ class UnsupportedMethod(Exception):
168
+ def __init__(self):
169
+ super().__init__(
170
+ 'Unsupported method for this kind of call',
171
+ )
pytgcalls/ffmpeg.py CHANGED
@@ -74,7 +74,7 @@ async def check_stream(
74
74
  codec_name = stream.get('codec_name', '')
75
75
  image_codecs = ['png', 'jpeg', 'jpg', 'mjpeg']
76
76
  if codec_type == 'video':
77
- is_image = codec_name in image_codecs
77
+ is_image &= codec_name in image_codecs
78
78
  have_video = True
79
79
  original_width = int(stream.get('width', 0))
80
80
  original_height = int(stream.get('height', 0))
@@ -270,7 +270,7 @@ def _extract_stream_params(command: List[str]):
270
270
  def _build_ffmpeg_options(
271
271
  stream_parameters: Union[AudioParameters, VideoParameters],
272
272
  ) -> List[str]:
273
- log_level = logging.getLogger().level
273
+ log_level = logging.getLogger('ffmpeg').level
274
274
  ffmpeg_level = 'info' if log_level == logging.DEBUG else 'quiet'
275
275
 
276
276
  options = ['-v', ffmpeg_level, '-f']
pytgcalls/filters.py CHANGED
@@ -7,9 +7,11 @@ from typing import Union
7
7
  from .mtproto import BridgedClient
8
8
  from .pytgcalls import PyTgCalls
9
9
  from .types import ChatUpdate
10
+ from .types import Device
11
+ from .types import Direction
10
12
  from .types import GroupCallParticipant
11
- from .types import StreamAudioEnded
12
- from .types import StreamVideoEnded
13
+ from .types import StreamEnded
14
+ from .types import StreamFrames
13
15
  from .types import Update
14
16
  from .types import UpdatedGroupCallParticipant
15
17
 
@@ -135,11 +137,27 @@ async def _me_filter(_, client: PyTgCalls, u: Update):
135
137
  me = create(_me_filter)
136
138
 
137
139
 
138
- async def _stream_filter(_, __, u: Update):
139
- return isinstance(u, (StreamVideoEnded, StreamAudioEnded))
140
-
140
+ # noinspection PyPep8Naming
141
+ class stream_end(Filter):
142
+ def __init__(
143
+ self,
144
+ stream_type: Optional[StreamEnded.Type] = None,
145
+ device: Optional[Device] = None,
146
+ ):
147
+ self.stream_type = stream_type
148
+ self.device = device
141
149
 
142
- stream_end = create(_stream_filter)
150
+ async def __call__(self, client: PyTgCalls, update: Update):
151
+ if isinstance(update, StreamEnded):
152
+ return (
153
+ (
154
+ self.stream_type is None or
155
+ self.stream_type & update.stream_type
156
+ ) and (
157
+ self.device is None or
158
+ self.device & update.device
159
+ )
160
+ )
143
161
 
144
162
 
145
163
  # noinspection PyPep8Naming
@@ -170,6 +188,7 @@ class chat_update(Filter):
170
188
  return False
171
189
 
172
190
 
191
+ # noinspection PyPep8Naming
173
192
  class call_participant(Filter):
174
193
  def __init__(self, flags: Optional[GroupCallParticipant.Action] = None):
175
194
  self.flags = flags
@@ -180,3 +199,27 @@ class call_participant(Filter):
180
199
  return True
181
200
  return self.flags & update.participant.action
182
201
  return False
202
+
203
+
204
+ # noinspection PyPep8Naming
205
+ class stream_frame(Filter):
206
+ def __init__(
207
+ self,
208
+ directions: Optional[Direction] = None,
209
+ devices: Optional[Device] = None,
210
+ ):
211
+ self.directions = directions
212
+ self.devices = devices
213
+
214
+ async def __call__(self, client: PyTgCalls, update: Update):
215
+ if isinstance(update, StreamFrames):
216
+ return (
217
+ (
218
+ self.directions is None or
219
+ self.directions & update.direction
220
+ ) and (
221
+ self.devices is None or
222
+ self.devices & update.device
223
+ )
224
+ )
225
+ return False
@@ -14,7 +14,7 @@ class HandlersHolder:
14
14
  def __init__(self):
15
15
  self._callbacks = []
16
16
 
17
- async def propagate(
17
+ async def _propagate(
18
18
  self,
19
19
  update,
20
20
  client=None,
@@ -1,9 +1,13 @@
1
1
  from .device_info import DeviceInfo
2
+ from .input_device import InputDevice
2
3
  from .media_devices import MediaDevices
3
- from .screen_info import ScreenInfo
4
+ from .screen_device import ScreenDevice
5
+ from .speaker_device import SpeakerDevice
4
6
 
5
7
  __all__ = (
6
8
  'DeviceInfo',
9
+ 'InputDevice',
7
10
  'MediaDevices',
8
- 'ScreenInfo',
11
+ 'ScreenDevice',
12
+ 'SpeakerDevice',
9
13
  )
@@ -1,20 +1,13 @@
1
- from sys import platform
2
-
3
-
4
1
  class DeviceInfo:
5
2
  def __init__(
6
3
  self,
7
- identifier: str,
8
- title: str,
4
+ name: str,
5
+ metadata: str,
6
+ is_video: bool,
9
7
  ):
10
- self.identifier = identifier
11
- self.title = title
12
- self.ffmpeg_parameters = ['-f']
8
+ self.title = name
9
+ self.metadata = metadata
10
+ self.is_video = is_video
13
11
 
14
- def build_ffmpeg_command(self):
15
- if platform == 'win32':
16
- self.ffmpeg_parameters += ['dshow']
17
- return f'audio={self.identifier}'
18
- else:
19
- self.ffmpeg_parameters += ['pulse']
20
- return self.identifier
12
+ def __repr__(self):
13
+ return self.title
@@ -0,0 +1,11 @@
1
+ from .device_info import DeviceInfo
2
+
3
+
4
+ class InputDevice(DeviceInfo):
5
+ def __init__(
6
+ self,
7
+ name: str,
8
+ metadata: str,
9
+ is_video: bool,
10
+ ):
11
+ super().__init__(name, metadata, is_video)
@@ -1,101 +1,50 @@
1
- import asyncio
2
- import re
3
- import subprocess
4
- from sys import platform
5
-
6
- from screeninfo import get_monitors
7
- from screeninfo import ScreenInfoError
1
+ from ntgcalls import DeviceInfo as RawDeviceInfo
2
+ from ntgcalls import NTgCalls
8
3
 
9
4
  from ..types.list import List
10
- from .device_info import DeviceInfo
11
- from .screen_info import ScreenInfo
5
+ from .input_device import InputDevice
6
+ from .screen_device import ScreenDevice
7
+ from .speaker_device import SpeakerDevice
12
8
 
13
9
 
14
10
  class MediaDevices:
15
11
  @staticmethod
16
- async def get_screen_devices() -> List:
17
- list_screens: List = List()
18
- if platform != 'darwin':
19
- try:
20
- for screen in get_monitors():
21
- list_screens.append(
22
- ScreenInfo(
23
- screen.x,
24
- screen.y,
25
- screen.width,
26
- screen.height,
27
- screen.is_primary,
28
- screen.name,
29
- ),
30
- )
31
- except ScreenInfoError:
32
- pass
33
- return list_screens
12
+ def _parse_devices(devices: list[RawDeviceInfo], is_video: bool) -> List:
13
+ return List(
14
+ InputDevice(device.name, device.metadata, is_video)
15
+ for device in devices
16
+ )
17
+
18
+ @staticmethod
19
+ def microphone_devices() -> List:
20
+ return MediaDevices._parse_devices(
21
+ NTgCalls.get_media_devices().microphone,
22
+ False,
23
+ )
24
+
25
+ @staticmethod
26
+ def speaker_devices() -> List:
27
+ return List(
28
+ SpeakerDevice(
29
+ device.name,
30
+ device.metadata,
31
+ )
32
+ for device in NTgCalls.get_media_devices().speaker
33
+ )
34
+
35
+ @staticmethod
36
+ def camera_devices() -> List:
37
+ return MediaDevices._parse_devices(
38
+ NTgCalls.get_media_devices().camera,
39
+ True,
40
+ )
34
41
 
35
42
  @staticmethod
36
- async def get_audio_devices() -> List:
37
- list_devices: List = List()
38
- if platform == 'darwin':
39
- return list_devices
40
- try:
41
- if platform == 'win32':
42
- commands = [
43
- 'ffmpeg',
44
- '-list_devices',
45
- 'true',
46
- '-f',
47
- 'dshow',
48
- '-i',
49
- 'dummy',
50
- ]
51
- else:
52
- commands = [
53
- 'pactl',
54
- 'list',
55
- 'sources',
56
- ]
57
- ffmpeg = await asyncio.create_subprocess_exec(
58
- *tuple(commands),
59
- stdout=asyncio.subprocess.PIPE,
60
- stderr=asyncio.subprocess.PIPE,
43
+ def screen_devices() -> List:
44
+ return List(
45
+ ScreenDevice(
46
+ device.name,
47
+ device.metadata,
61
48
  )
62
- try:
63
- stdout, stderr = await asyncio.wait_for(
64
- ffmpeg.communicate(),
65
- timeout=3,
66
- )
67
- result: str = ''
68
- if platform == 'win32':
69
- result = stderr.decode('utf-8')
70
- elif platform != 'darwin':
71
- result = stdout.decode('utf-8')
72
- except subprocess.TimeoutExpired:
73
- return list_devices
74
- if platform == 'win32':
75
- list_raw = result.split('DirectShow audio devices')
76
- if len(list_raw) < 2:
77
- return list_devices
78
- output = re.findall(
79
- '\\[.*?].*?"(.*?)".*?\n\\[.*?].*?"(.*?)"', list_raw[1],
80
- )
81
- for device in output:
82
- list_devices.append(
83
- DeviceInfo(
84
- device[1],
85
- device[0],
86
- ),
87
- )
88
- else:
89
- output = re.findall(
90
- 'Name: (.*?)\n.*?Description: (.*?)\n', result,
91
- )
92
- for device in output:
93
- list_devices.append(
94
- DeviceInfo(
95
- device[0],
96
- device[1],
97
- ),
98
- )
99
- except FileNotFoundError:
100
- pass
101
- return list_devices
49
+ for device in NTgCalls.get_media_devices().screen
50
+ )
@@ -0,0 +1,10 @@
1
+ from .input_device import InputDevice
2
+
3
+
4
+ class ScreenDevice(InputDevice):
5
+ def __init__(
6
+ self,
7
+ name: str,
8
+ metadata: str,
9
+ ):
10
+ super().__init__(name, metadata, True)
@@ -0,0 +1,10 @@
1
+ from .device_info import DeviceInfo
2
+
3
+
4
+ class SpeakerDevice(DeviceInfo):
5
+ def __init__(
6
+ self,
7
+ name: str,
8
+ metadata: str,
9
+ ):
10
+ super().__init__(name, metadata, False)
@@ -1,6 +1,7 @@
1
1
  from typing import Union
2
2
 
3
3
  from ...exceptions import NoActiveGroupCall
4
+ from ...exceptions import UnsupportedMethod
4
5
  from ...mtproto_required import mtproto_required
5
6
  from ...scaffold import Scaffold
6
7
  from ...statictypes import statictypes
@@ -15,6 +16,8 @@ class ChangeVolumeCall(Scaffold):
15
16
  volume: int,
16
17
  ):
17
18
  chat_id = await self.resolve_chat_id(chat_id)
19
+ if chat_id >= 0: # type: ignore
20
+ raise UnsupportedMethod()
18
21
  chat_call = await self._app.get_full_chat(
19
22
  chat_id,
20
23
  )
@@ -2,6 +2,7 @@ from typing import List
2
2
  from typing import Optional
3
3
  from typing import Union
4
4
 
5
+ from ...exceptions import UnsupportedMethod
5
6
  from ...mtproto_required import mtproto_required
6
7
  from ...scaffold import Scaffold
7
8
  from ...statictypes import statictypes
@@ -15,6 +16,9 @@ class GetParticipants(Scaffold):
15
16
  self,
16
17
  chat_id: Union[int, str],
17
18
  ) -> Optional[List[GroupCallParticipant]]:
19
+ chat_id = await self.resolve_chat_id(chat_id)
20
+ if chat_id >= 0: # type: ignore
21
+ raise UnsupportedMethod()
18
22
  return await self._app.get_group_call_participants(
19
- await self.resolve_chat_id(chat_id),
23
+ chat_id,
20
24
  )
@@ -40,9 +40,11 @@ class LeaveCall(Scaffold):
40
40
  chat_id,
41
41
  )
42
42
  else:
43
- await self._app.discard_call(chat_id)
43
+ await self._app.discard_call(chat_id, False)
44
44
  if is_p2p_waiting:
45
45
  self._p2p_configs.pop(chat_id)
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)