py-tgcalls 2.1.0.dev3__py3-none-any.whl → 2.1.0.dev5__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.0.dev3.dist-info → py_tgcalls-2.1.0.dev5.dist-info}/METADATA +2 -2
- {py_tgcalls-2.1.0.dev3.dist-info → py_tgcalls-2.1.0.dev5.dist-info}/RECORD +25 -23
- pytgcalls/__version__.py +1 -1
- pytgcalls/ffmpeg.py +2 -2
- pytgcalls/methods/calls/leave_call.py +1 -1
- pytgcalls/methods/stream/__init__.py +2 -0
- pytgcalls/methods/stream/play.py +3 -2
- pytgcalls/methods/stream/send_frame.py +38 -0
- pytgcalls/methods/utilities/start.py +1 -0
- pytgcalls/methods/utilities/stream_params.py +6 -0
- pytgcalls/mtproto/bridged_client.py +1 -0
- pytgcalls/mtproto/hydrogram_client.py +8 -1
- pytgcalls/mtproto/mtproto_client.py +2 -0
- pytgcalls/mtproto/pyrogram_client.py +8 -1
- pytgcalls/mtproto/telethon_client.py +25 -3
- pytgcalls/types/__init__.py +2 -0
- pytgcalls/types/stream/__init__.py +2 -0
- pytgcalls/types/stream/device.py +12 -0
- pytgcalls/types/stream/direction.py +2 -2
- pytgcalls/types/stream/external_media.py +8 -0
- pytgcalls/types/stream/media_stream.py +54 -6
- pytgcalls/types/stream/stream_frame.py +4 -4
- {py_tgcalls-2.1.0.dev3.dist-info → py_tgcalls-2.1.0.dev5.dist-info}/LICENSE +0 -0
- {py_tgcalls-2.1.0.dev3.dist-info → py_tgcalls-2.1.0.dev5.dist-info}/WHEEL +0 -0
- {py_tgcalls-2.1.0.dev3.dist-info → py_tgcalls-2.1.0.dev5.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: py-tgcalls
|
|
3
|
-
Version: 2.1.0.
|
|
3
|
+
Version: 2.1.0.dev5
|
|
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.
|
|
190
|
+
Requires-Dist: ntgcalls<1.4.0,>=1.3.0b7
|
|
191
191
|
Requires-Dist: deprecation
|
|
192
192
|
Provides-Extra: pyrogram
|
|
193
193
|
Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
|
|
2
|
-
pytgcalls/__version__.py,sha256=
|
|
2
|
+
pytgcalls/__version__.py,sha256=KQAc7KU-mJvztNT933s927lrNsCxny7RGtGEb4R-foc,27
|
|
3
3
|
pytgcalls/environment.py,sha256=ctCHACvG6l8SdpPewSBhOvc70kbwpv18maC0TwLvZ08,1924
|
|
4
4
|
pytgcalls/exceptions.py,sha256=0MmAktc53ajYAc7ThjD2tJ9PDyibUi0iHZMfUy2IoKs,4109
|
|
5
|
-
pytgcalls/ffmpeg.py,sha256=
|
|
5
|
+
pytgcalls/ffmpeg.py,sha256=tm6DBxyNfPh3h3an-b2s9x1UyX-cvkCdov9prlXxVZY,8649
|
|
6
6
|
pytgcalls/filters.py,sha256=dHRXgxhQoLaLsSAsYYaq4jCwVran9sBWBG1txKWIOjY,6039
|
|
7
7
|
pytgcalls/mtproto_required.py,sha256=6B-31p5qH_6oekUgypV4nK3hqPS6Nr-pA8S81wjnbaY,630
|
|
8
8
|
pytgcalls/mutex.py,sha256=Frjji5Ctzlk4AXEBuBLnDK-7HbtreoV6zuyKpFpMNI4,236
|
|
@@ -27,15 +27,16 @@ 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=
|
|
30
|
+
pytgcalls/methods/calls/leave_call.py,sha256=dJg5JWmbTIKQJsCccvea4Ma-SaSKVZcRVRqkmsWOLx8,1376
|
|
31
31
|
pytgcalls/methods/decorators/__init__.py,sha256=TCGaEVZnHjtOwv-3PNfaCVm0kyFhJApUPUNntt6MwyM,78
|
|
32
32
|
pytgcalls/methods/decorators/on_update.py,sha256=ZTL4YcQk0N4Ru56a5WItUvkSN5SAqr6_RDZvXmZMIHs,316
|
|
33
|
-
pytgcalls/methods/stream/__init__.py,sha256=
|
|
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=
|
|
36
|
+
pytgcalls/methods/stream/play.py,sha256=1NiAIKBdSsiighc74EKHQv_Fl_ehWLYgGPq5x62s0sE,10189
|
|
37
37
|
pytgcalls/methods/stream/record.py,sha256=toW1LtgUMiaw-KGe9DnWTRsGYLhoKlG0r8eJphMhGIA,1212
|
|
38
38
|
pytgcalls/methods/stream/resume_stream.py,sha256=z_DgP4cDExjEqEeX_ZL--50MXQ9lrATK876SIwE71PQ,576
|
|
39
|
+
pytgcalls/methods/stream/send_frame.py,sha256=TAHXfKgBrX-nT9x-08GZNOeObqyhtgGYi7i4kuGHO6c,1080
|
|
39
40
|
pytgcalls/methods/stream/time.py,sha256=5y9TMBf_d6YPLbMcGx3yMZQUZdo8zb5fQb9STsh7R3Y,656
|
|
40
41
|
pytgcalls/methods/stream/unmute_stream.py,sha256=KUMhfMbhsPmZsmpF4cGWC1FVW7YwXha2MmQnqrBhM8s,576
|
|
41
42
|
pytgcalls/methods/utilities/__init__.py,sha256=JcKwqNo6fFXXfuab94fNEraKF1P9fnSSgr0WQDRjF2w,339
|
|
@@ -47,16 +48,16 @@ pytgcalls/methods/utilities/idle.py,sha256=MDdzHTv1ws2yBhsvhBUnssGdghkZ2KwR0HUCP
|
|
|
47
48
|
pytgcalls/methods/utilities/ping.py,sha256=hhIMSHk2BzMB-IKpwLdZFVrsEvGm2ftJwKLs1k4anh8,244
|
|
48
49
|
pytgcalls/methods/utilities/resolve_chat_id.py,sha256=92x2LHbUlnJMm-kS3fXOYmzYpY2TZbqtQD2rw3eBXDY,382
|
|
49
50
|
pytgcalls/methods/utilities/run.py,sha256=cnYQd2xB5Cr_WS0Q2cXJZPGiN6JOCULzj1r4xXVyrlg,152
|
|
50
|
-
pytgcalls/methods/utilities/start.py,sha256=
|
|
51
|
-
pytgcalls/methods/utilities/stream_params.py,sha256=
|
|
51
|
+
pytgcalls/methods/utilities/start.py,sha256=pG2I3bSWDBn7qNTxwbSCvkwpkhWAYKlAqJ5TzapMgCc,13406
|
|
52
|
+
pytgcalls/methods/utilities/stream_params.py,sha256=V9bKF3w4XLsbzYNAuCXrcjrojioGHLlFeZ3Jz9H3z-0,3235
|
|
52
53
|
pytgcalls/mtproto/__init__.py,sha256=X4zvzFG7km7qHyE0fdvA550WcOVO_xl_p__gvIfDGmw,130
|
|
53
|
-
pytgcalls/mtproto/bridged_client.py,sha256=
|
|
54
|
+
pytgcalls/mtproto/bridged_client.py,sha256=69d59-Vm_CSexd7maHIeGc9BUCiBLoa5YKZy_y-a540,5868
|
|
54
55
|
pytgcalls/mtproto/client_cache.py,sha256=Mt0827e_T8DXJHOTkXhkIQUT9EUBWjoLcFcXP1gBnZY,5973
|
|
55
|
-
pytgcalls/mtproto/hydrogram_client.py,sha256=
|
|
56
|
-
pytgcalls/mtproto/mtproto_client.py,sha256=
|
|
57
|
-
pytgcalls/mtproto/pyrogram_client.py,sha256=
|
|
58
|
-
pytgcalls/mtproto/telethon_client.py,sha256=
|
|
59
|
-
pytgcalls/types/__init__.py,sha256=
|
|
56
|
+
pytgcalls/mtproto/hydrogram_client.py,sha256=HWYhScpVd7dwfuwngYcohHcNNnPZxcQA0mHb_HAp7SE,23020
|
|
57
|
+
pytgcalls/mtproto/mtproto_client.py,sha256=1C1Cc1GOrKom-70NqUOICKxjfjgpPZBSdKdwFUjZzBc,7616
|
|
58
|
+
pytgcalls/mtproto/pyrogram_client.py,sha256=Z24edwj8wApTqECW5o5fF257fZhEIFvMw69uSULXlPU,23202
|
|
59
|
+
pytgcalls/mtproto/telethon_client.py,sha256=U9FeII1-nmKCJlAekpAQ7Ljb9doHD0aFlKSbMjlszls,22440
|
|
60
|
+
pytgcalls/types/__init__.py,sha256=6DkID8ItCV7G6PjkKBMCgUEWfSHbMJjn6lvJ8ysIq7U,1086
|
|
60
61
|
pytgcalls/types/browsers.py,sha256=47Kr5q96n4Q4WvVhA6IUlS2egEcA9GRLlDeFcQYyc9M,9545
|
|
61
62
|
pytgcalls/types/cache.py,sha256=FfsOcmYnsBGPlJoTPIXXYcUSpGE3rhx6cjIH77hyUL0,1059
|
|
62
63
|
pytgcalls/types/dict.py,sha256=lAo9hu4VlVJa9S7P8Y81BYmKtvz0rH7hwpGcH3ynHUw,78
|
|
@@ -83,17 +84,18 @@ pytgcalls/types/raw/audio_stream.py,sha256=oN7Sx9oLbNFuNXiGYpoNabMwqWKGquLiHEywe
|
|
|
83
84
|
pytgcalls/types/raw/stream.py,sha256=xJ3w77ofKFnLIA4cAWIuw2yREpMPvTzIvtei-3xEbJQ,666
|
|
84
85
|
pytgcalls/types/raw/video_parameters.py,sha256=nUl9gkfYTVU0iLNGTtlZ5cZg8K6F7odIi9n8POJXCK4,639
|
|
85
86
|
pytgcalls/types/raw/video_stream.py,sha256=uE3jU9kJsrAoefXtDtEKNqTjcYGaQbZ1gbJ1SfiVWIc,488
|
|
86
|
-
pytgcalls/types/stream/__init__.py,sha256=
|
|
87
|
+
pytgcalls/types/stream/__init__.py,sha256=YCJ2sg0ebhFMUzugj4V7SvvqDeeTvc0n_evdnhYFHls,520
|
|
87
88
|
pytgcalls/types/stream/audio_quality.py,sha256=4X94ErmTeLP4TVcE3eLtPPdtluSPxgxbgTosuNJOVhc,141
|
|
88
|
-
pytgcalls/types/stream/device.py,sha256=
|
|
89
|
-
pytgcalls/types/stream/direction.py,sha256=
|
|
90
|
-
pytgcalls/types/stream/
|
|
89
|
+
pytgcalls/types/stream/device.py,sha256=EdoDg6lPE7fgoZI04Nr0E9zbIk-iRIBgYYAzVqoCBPM,961
|
|
90
|
+
pytgcalls/types/stream/direction.py,sha256=gd10wUmpfsqx87kCAPZt6u8pFiPb09WZfHKcMWAZokU,394
|
|
91
|
+
pytgcalls/types/stream/external_media.py,sha256=RiuSX5tZGdNsQZ8LIRk5Lp4Ksv9oTvaccmInJRZYo4M,114
|
|
92
|
+
pytgcalls/types/stream/media_stream.py,sha256=sFlP8BMmCjhYp_A8U7XkGV4jbxR7PYsmJiII5Fxs0_A,11971
|
|
91
93
|
pytgcalls/types/stream/record_stream.py,sha256=pp08OahflXJ5Cs281KxU-K2ULRP88ega5WBkJ7dwBjk,3038
|
|
92
94
|
pytgcalls/types/stream/stream_ended.py,sha256=xR_kZwFf03hA6rw_nvI7Be7GwoCKzQf_1MKaGpPDXqY,716
|
|
93
|
-
pytgcalls/types/stream/stream_frame.py,sha256=
|
|
95
|
+
pytgcalls/types/stream/stream_frame.py,sha256=NuRaGoRvy4icJruG8ejQQoYVV282JxlIcSB-MQUIWYI,813
|
|
94
96
|
pytgcalls/types/stream/video_quality.py,sha256=HBfWq005kh-D19MaVE9VzVdnODzrXf4IJUimCfslfiU,231
|
|
95
|
-
py_tgcalls-2.1.0.
|
|
96
|
-
py_tgcalls-2.1.0.
|
|
97
|
-
py_tgcalls-2.1.0.
|
|
98
|
-
py_tgcalls-2.1.0.
|
|
99
|
-
py_tgcalls-2.1.0.
|
|
97
|
+
py_tgcalls-2.1.0.dev5.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
98
|
+
py_tgcalls-2.1.0.dev5.dist-info/METADATA,sha256=gg0QIm1PMKjSbyvLHlt8HYlleLRCcNYuP9kO_ckaQjA,14399
|
|
99
|
+
py_tgcalls-2.1.0.dev5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
100
|
+
py_tgcalls-2.1.0.dev5.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
|
|
101
|
+
py_tgcalls-2.1.0.dev5.dist-info/RECORD,,
|
pytgcalls/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '2.1.0.
|
|
1
|
+
__version__ = '2.1.0.dev5'
|
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
|
|
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']
|
|
@@ -3,6 +3,7 @@ from .pause_stream import PauseStream
|
|
|
3
3
|
from .play import Play
|
|
4
4
|
from .record import Record
|
|
5
5
|
from .resume_stream import ResumeStream
|
|
6
|
+
from .send_frame import SendFrame
|
|
6
7
|
from .time import Time
|
|
7
8
|
from .unmute_stream import UnMuteStream
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ class StreamMethods(
|
|
|
12
13
|
PauseStream,
|
|
13
14
|
Play,
|
|
14
15
|
Record,
|
|
16
|
+
SendFrame,
|
|
15
17
|
Time,
|
|
16
18
|
ResumeStream,
|
|
17
19
|
UnMuteStream,
|
pytgcalls/methods/stream/play.py
CHANGED
|
@@ -5,9 +5,9 @@ from typing import Union
|
|
|
5
5
|
|
|
6
6
|
from ntgcalls import ConnectionNotFound
|
|
7
7
|
from ntgcalls import FileError
|
|
8
|
-
from ntgcalls import InvalidParams
|
|
9
8
|
from ntgcalls import StreamMode
|
|
10
9
|
from ntgcalls import TelegramServerError
|
|
10
|
+
from ntgcalls import TransportParseException
|
|
11
11
|
|
|
12
12
|
from ...exceptions import NoActiveGroupCall
|
|
13
13
|
from ...exceptions import TimedOutAnswer
|
|
@@ -159,6 +159,7 @@ class Play(Scaffold):
|
|
|
159
159
|
await self._binding.stop(chat_id)
|
|
160
160
|
except ConnectionNotFound:
|
|
161
161
|
pass
|
|
162
|
+
await self._app.discard_call(chat_id, True)
|
|
162
163
|
raise TimedOutAnswer()
|
|
163
164
|
finally:
|
|
164
165
|
self._p2p_configs.pop(chat_id, None)
|
|
@@ -238,7 +239,7 @@ class Play(Scaffold):
|
|
|
238
239
|
self._need_unmute.add(chat_id)
|
|
239
240
|
except FileError as e:
|
|
240
241
|
raise FileNotFoundError(e)
|
|
241
|
-
except
|
|
242
|
+
except TransportParseException:
|
|
242
243
|
raise UnMuteNeeded()
|
|
243
244
|
except Exception:
|
|
244
245
|
if isinstance(config, GroupCallConfig):
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from ntgcalls import ConnectionNotFound
|
|
4
|
+
from ntgcalls import FrameData
|
|
5
|
+
|
|
6
|
+
from ...exceptions import NotInCallError
|
|
7
|
+
from ...mtproto_required import mtproto_required
|
|
8
|
+
from ...scaffold import Scaffold
|
|
9
|
+
from ...statictypes import statictypes
|
|
10
|
+
from ...types import Device
|
|
11
|
+
from ...types import StreamFrame
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SendFrame(Scaffold):
|
|
15
|
+
@statictypes
|
|
16
|
+
@mtproto_required
|
|
17
|
+
async def send_frame(
|
|
18
|
+
self,
|
|
19
|
+
chat_id: Union[int, str],
|
|
20
|
+
device: Device,
|
|
21
|
+
data: bytes,
|
|
22
|
+
frame_data: StreamFrame.Info = StreamFrame.Info(),
|
|
23
|
+
):
|
|
24
|
+
chat_id = await self.resolve_chat_id(chat_id)
|
|
25
|
+
try:
|
|
26
|
+
return await self._binding.send_external_frame(
|
|
27
|
+
chat_id,
|
|
28
|
+
Device.to_raw(device),
|
|
29
|
+
data,
|
|
30
|
+
FrameData(
|
|
31
|
+
frame_data.capture_time,
|
|
32
|
+
frame_data.rotation,
|
|
33
|
+
frame_data.width,
|
|
34
|
+
frame_data.height,
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
except ConnectionNotFound:
|
|
38
|
+
raise NotInCallError()
|
|
@@ -5,6 +5,7 @@ from ntgcalls import AudioDescription
|
|
|
5
5
|
from ntgcalls import MediaDescription
|
|
6
6
|
from ntgcalls import VideoDescription
|
|
7
7
|
|
|
8
|
+
from ...types import RecordStream
|
|
8
9
|
from ...types.raw import AudioStream
|
|
9
10
|
from ...types.raw import Stream
|
|
10
11
|
from ...types.raw import VideoStream
|
|
@@ -19,6 +20,11 @@ class StreamParams:
|
|
|
19
20
|
if stream is not None:
|
|
20
21
|
if isinstance(stream, MediaStream):
|
|
21
22
|
await stream.check_stream()
|
|
23
|
+
elif isinstance(stream, RecordStream):
|
|
24
|
+
raise ValueError(
|
|
25
|
+
'Stream should be an instance of '
|
|
26
|
+
'MediaStream or a raw Stream',
|
|
27
|
+
)
|
|
22
28
|
|
|
23
29
|
return MediaDescription(
|
|
24
30
|
microphone=StreamParams._parse_media_description(
|
|
@@ -43,6 +43,7 @@ from hydrogram.raw.types import PhoneCall
|
|
|
43
43
|
from hydrogram.raw.types import PhoneCallAccepted
|
|
44
44
|
from hydrogram.raw.types import PhoneCallDiscarded
|
|
45
45
|
from hydrogram.raw.types import PhoneCallDiscardReasonHangup
|
|
46
|
+
from hydrogram.raw.types import PhoneCallDiscardReasonMissed
|
|
46
47
|
from hydrogram.raw.types import PhoneCallProtocol
|
|
47
48
|
from hydrogram.raw.types import PhoneCallRequested
|
|
48
49
|
from hydrogram.raw.types import PhoneCallWaiting
|
|
@@ -558,15 +559,21 @@ class HydrogramClient(BridgedClient):
|
|
|
558
559
|
async def discard_call(
|
|
559
560
|
self,
|
|
560
561
|
chat_id: int,
|
|
562
|
+
is_missed: bool,
|
|
561
563
|
):
|
|
562
564
|
peer = self._cache.get_phone_call(chat_id)
|
|
563
565
|
if peer is None:
|
|
564
566
|
return
|
|
567
|
+
reason = (
|
|
568
|
+
PhoneCallDiscardReasonMissed()
|
|
569
|
+
if is_missed
|
|
570
|
+
else PhoneCallDiscardReasonHangup()
|
|
571
|
+
)
|
|
565
572
|
await self._app.invoke(
|
|
566
573
|
DiscardCall(
|
|
567
574
|
peer=peer,
|
|
568
575
|
duration=0,
|
|
569
|
-
reason=
|
|
576
|
+
reason=reason,
|
|
570
577
|
connection_id=0,
|
|
571
578
|
video=False,
|
|
572
579
|
),
|
|
@@ -126,10 +126,12 @@ class MtProtoClient:
|
|
|
126
126
|
async def discard_call(
|
|
127
127
|
self,
|
|
128
128
|
user_id: int,
|
|
129
|
+
is_missed: bool,
|
|
129
130
|
):
|
|
130
131
|
if self._bind_client is not None:
|
|
131
132
|
return await self._bind_client.discard_call(
|
|
132
133
|
user_id,
|
|
134
|
+
is_missed,
|
|
133
135
|
)
|
|
134
136
|
else:
|
|
135
137
|
raise InvalidMTProtoClient()
|
|
@@ -45,6 +45,7 @@ from pyrogram.raw.types import PhoneCall
|
|
|
45
45
|
from pyrogram.raw.types import PhoneCallAccepted
|
|
46
46
|
from pyrogram.raw.types import PhoneCallDiscarded
|
|
47
47
|
from pyrogram.raw.types import PhoneCallDiscardReasonHangup
|
|
48
|
+
from pyrogram.raw.types import PhoneCallDiscardReasonMissed
|
|
48
49
|
from pyrogram.raw.types import PhoneCallProtocol
|
|
49
50
|
from pyrogram.raw.types import PhoneCallRequested
|
|
50
51
|
from pyrogram.raw.types import PhoneCallWaiting
|
|
@@ -566,15 +567,21 @@ class PyrogramClient(BridgedClient):
|
|
|
566
567
|
async def discard_call(
|
|
567
568
|
self,
|
|
568
569
|
chat_id: int,
|
|
570
|
+
is_missed: bool,
|
|
569
571
|
):
|
|
570
572
|
peer = self._cache.get_phone_call(chat_id)
|
|
571
573
|
if peer is None:
|
|
572
574
|
return
|
|
575
|
+
reason = (
|
|
576
|
+
PhoneCallDiscardReasonMissed()
|
|
577
|
+
if is_missed
|
|
578
|
+
else PhoneCallDiscardReasonHangup()
|
|
579
|
+
)
|
|
573
580
|
await self._app.invoke(
|
|
574
581
|
DiscardCall(
|
|
575
582
|
peer=peer,
|
|
576
583
|
duration=0,
|
|
577
|
-
reason=
|
|
584
|
+
reason=reason,
|
|
578
585
|
connection_id=0,
|
|
579
586
|
video=False,
|
|
580
587
|
),
|
|
@@ -34,11 +34,13 @@ from telethon.tl.types import InputPhoneCall
|
|
|
34
34
|
from telethon.tl.types import MessageActionChatDeleteUser
|
|
35
35
|
from telethon.tl.types import MessageActionInviteToGroupCall
|
|
36
36
|
from telethon.tl.types import MessageService
|
|
37
|
+
from telethon.tl.types import PeerChannel
|
|
37
38
|
from telethon.tl.types import PeerChat
|
|
38
39
|
from telethon.tl.types import PhoneCall
|
|
39
40
|
from telethon.tl.types import PhoneCallAccepted
|
|
40
41
|
from telethon.tl.types import PhoneCallDiscarded
|
|
41
42
|
from telethon.tl.types import PhoneCallDiscardReasonHangup
|
|
43
|
+
from telethon.tl.types import PhoneCallDiscardReasonMissed
|
|
42
44
|
from telethon.tl.types import PhoneCallProtocol
|
|
43
45
|
from telethon.tl.types import PhoneCallRequested
|
|
44
46
|
from telethon.tl.types import PhoneCallWaiting
|
|
@@ -182,7 +184,9 @@ class TelethonClient(BridgedClient):
|
|
|
182
184
|
UpdateGroupCall,
|
|
183
185
|
):
|
|
184
186
|
chat_id = self.chat_id(
|
|
185
|
-
await self.
|
|
187
|
+
await self._get_entity_group(
|
|
188
|
+
update.chat_id,
|
|
189
|
+
),
|
|
186
190
|
)
|
|
187
191
|
if isinstance(
|
|
188
192
|
update.call,
|
|
@@ -215,7 +219,9 @@ class TelethonClient(BridgedClient):
|
|
|
215
219
|
):
|
|
216
220
|
chat_id = self.chat_id(update)
|
|
217
221
|
try:
|
|
218
|
-
await self._app.get_entity(
|
|
222
|
+
await self._app.get_entity(
|
|
223
|
+
PeerChannel(chat_id),
|
|
224
|
+
)
|
|
219
225
|
except ChannelPrivateError:
|
|
220
226
|
self._cache.drop_cache(chat_id)
|
|
221
227
|
await self.propagate(
|
|
@@ -274,6 +280,16 @@ class TelethonClient(BridgedClient):
|
|
|
274
280
|
),
|
|
275
281
|
)
|
|
276
282
|
|
|
283
|
+
async def _get_entity_group(self, chat_id):
|
|
284
|
+
try:
|
|
285
|
+
return await self._app.get_entity(
|
|
286
|
+
PeerChannel(chat_id),
|
|
287
|
+
)
|
|
288
|
+
except ValueError:
|
|
289
|
+
return await self._app.get_entity(
|
|
290
|
+
PeerChat(chat_id),
|
|
291
|
+
)
|
|
292
|
+
|
|
277
293
|
async def get_call(
|
|
278
294
|
self,
|
|
279
295
|
chat_id: int,
|
|
@@ -535,15 +551,21 @@ class TelethonClient(BridgedClient):
|
|
|
535
551
|
async def discard_call(
|
|
536
552
|
self,
|
|
537
553
|
chat_id: int,
|
|
554
|
+
is_missed: bool,
|
|
538
555
|
):
|
|
539
556
|
peer = self._cache.get_phone_call(chat_id)
|
|
540
557
|
if peer is None:
|
|
541
558
|
return
|
|
559
|
+
reason = (
|
|
560
|
+
PhoneCallDiscardReasonMissed()
|
|
561
|
+
if is_missed
|
|
562
|
+
else PhoneCallDiscardReasonHangup()
|
|
563
|
+
)
|
|
542
564
|
await self._app(
|
|
543
565
|
DiscardCallRequest(
|
|
544
566
|
peer=peer,
|
|
545
567
|
duration=0,
|
|
546
|
-
reason=
|
|
568
|
+
reason=reason,
|
|
547
569
|
connection_id=0,
|
|
548
570
|
video=False,
|
|
549
571
|
),
|
pytgcalls/types/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from .chats import UpdatedGroupCallParticipant
|
|
|
12
12
|
from .stream import AudioQuality
|
|
13
13
|
from .stream import Device
|
|
14
14
|
from .stream import Direction
|
|
15
|
+
from .stream import ExternalMedia
|
|
15
16
|
from .stream import MediaStream
|
|
16
17
|
from .stream import RecordStream
|
|
17
18
|
from .stream import StreamEnded
|
|
@@ -23,6 +24,7 @@ __all__ = (
|
|
|
23
24
|
'AudioQuality',
|
|
24
25
|
'Device',
|
|
25
26
|
'Direction',
|
|
27
|
+
'ExternalMedia',
|
|
26
28
|
'Browsers',
|
|
27
29
|
'Cache',
|
|
28
30
|
'ChatUpdate',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .audio_quality import AudioQuality
|
|
2
2
|
from .device import Device
|
|
3
3
|
from .direction import Direction
|
|
4
|
+
from .external_media import ExternalMedia
|
|
4
5
|
from .media_stream import MediaStream
|
|
5
6
|
from .record_stream import RecordStream
|
|
6
7
|
from .stream_ended import StreamEnded
|
|
@@ -11,6 +12,7 @@ __all__ = (
|
|
|
11
12
|
'AudioQuality',
|
|
12
13
|
'Device',
|
|
13
14
|
'Direction',
|
|
15
|
+
'ExternalMedia',
|
|
14
16
|
'MediaStream',
|
|
15
17
|
'RecordStream',
|
|
16
18
|
'StreamEnded',
|
pytgcalls/types/stream/device.py
CHANGED
|
@@ -22,3 +22,15 @@ class Device(Flag):
|
|
|
22
22
|
if device == StreamDevice.SCREEN:
|
|
23
23
|
return Device.SCREEN
|
|
24
24
|
return None
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def to_raw(device: 'Device'):
|
|
28
|
+
if device == Device.MICROPHONE:
|
|
29
|
+
return StreamDevice.MICROPHONE
|
|
30
|
+
if device == Device.SPEAKER:
|
|
31
|
+
return StreamDevice.SPEAKER
|
|
32
|
+
if device == Device.CAMERA:
|
|
33
|
+
return StreamDevice.CAMERA
|
|
34
|
+
if device == Device.SCREEN:
|
|
35
|
+
return StreamDevice.SCREEN
|
|
36
|
+
return None
|
|
@@ -11,8 +11,8 @@ class Direction(Flag):
|
|
|
11
11
|
|
|
12
12
|
@staticmethod
|
|
13
13
|
def from_raw(direction: StreamMode):
|
|
14
|
-
if direction == StreamMode.
|
|
14
|
+
if direction == StreamMode.CAPTURE:
|
|
15
15
|
return Direction.OUTGOING
|
|
16
|
-
if direction == StreamMode.
|
|
16
|
+
if direction == StreamMode.PLAYBACK:
|
|
17
17
|
return Direction.INCOMING
|
|
18
18
|
return None
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from enum import auto
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Dict
|
|
@@ -24,6 +25,9 @@ from ..raw.video_parameters import VideoParameters
|
|
|
24
25
|
from ..raw.video_stream import VideoStream
|
|
25
26
|
from ..stream.audio_quality import AudioQuality
|
|
26
27
|
from ..stream.video_quality import VideoQuality
|
|
28
|
+
from .external_media import ExternalMedia
|
|
29
|
+
|
|
30
|
+
py_logger = logging.getLogger('pytgcalls')
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class MediaStream(Stream):
|
|
@@ -35,7 +39,7 @@ class MediaStream(Stream):
|
|
|
35
39
|
@statictypes
|
|
36
40
|
def __init__(
|
|
37
41
|
self,
|
|
38
|
-
media_path: Union[str, Path, InputDevice],
|
|
42
|
+
media_path: Union[str, Path, InputDevice, ExternalMedia],
|
|
39
43
|
audio_parameters: Union[
|
|
40
44
|
AudioParameters,
|
|
41
45
|
AudioQuality,
|
|
@@ -44,7 +48,12 @@ class MediaStream(Stream):
|
|
|
44
48
|
VideoParameters,
|
|
45
49
|
VideoQuality,
|
|
46
50
|
] = VideoQuality.SD_480p,
|
|
47
|
-
audio_path: Optional[
|
|
51
|
+
audio_path: Optional[
|
|
52
|
+
Union[
|
|
53
|
+
str, Path,
|
|
54
|
+
InputDevice, ExternalMedia,
|
|
55
|
+
]
|
|
56
|
+
] = None,
|
|
48
57
|
audio_flags: Optional[Flags] = Flags.AUTO_DETECT,
|
|
49
58
|
video_flags: Optional[Flags] = Flags.AUTO_DETECT,
|
|
50
59
|
headers: Optional[Dict[str, str]] = None,
|
|
@@ -70,10 +79,17 @@ class MediaStream(Stream):
|
|
|
70
79
|
self._audio_path: Optional[str] = None
|
|
71
80
|
self._is_media_device: bool = False
|
|
72
81
|
self._is_audio_device: bool = False
|
|
82
|
+
self._is_audio_external: bool = False
|
|
83
|
+
self._is_video_external: bool = False
|
|
73
84
|
if isinstance(media_path, str):
|
|
74
85
|
self._media_path = media_path
|
|
75
86
|
elif isinstance(media_path, Path):
|
|
76
87
|
self._media_path = str(media_path)
|
|
88
|
+
elif isinstance(media_path, ExternalMedia):
|
|
89
|
+
if media_path & ExternalMedia.AUDIO:
|
|
90
|
+
self._is_audio_external = True
|
|
91
|
+
if media_path & ExternalMedia.VIDEO:
|
|
92
|
+
self._is_video_external = True
|
|
77
93
|
elif isinstance(media_path, (InputDevice, ScreenDevice)):
|
|
78
94
|
print('MediaStream', media_path.is_video)
|
|
79
95
|
if media_path.is_video:
|
|
@@ -87,6 +103,17 @@ class MediaStream(Stream):
|
|
|
87
103
|
self._audio_path = audio_path
|
|
88
104
|
elif isinstance(audio_path, Path):
|
|
89
105
|
self._audio_path = str(audio_path)
|
|
106
|
+
elif isinstance(audio_path, ExternalMedia):
|
|
107
|
+
self._audio_path = ''
|
|
108
|
+
if audio_path == ExternalMedia.AUDIO:
|
|
109
|
+
if self._is_audio_external:
|
|
110
|
+
py_logger.warning(
|
|
111
|
+
'Audio path is already an audio source, '
|
|
112
|
+
'ignoring audio path',
|
|
113
|
+
)
|
|
114
|
+
self._is_audio_external = True
|
|
115
|
+
else:
|
|
116
|
+
raise ValueError('Audio path must be an audio source')
|
|
90
117
|
elif isinstance(audio_path, (InputDevice, ScreenDevice)):
|
|
91
118
|
if audio_path.is_video:
|
|
92
119
|
raise ValueError('Audio path must be an audio device')
|
|
@@ -100,13 +127,23 @@ class MediaStream(Stream):
|
|
|
100
127
|
self._headers = headers
|
|
101
128
|
super().__init__(
|
|
102
129
|
microphone=None
|
|
103
|
-
if
|
|
130
|
+
if (
|
|
131
|
+
self._audio_flags & MediaStream.Flags.IGNORE or
|
|
132
|
+
self._media_path is None and
|
|
133
|
+
self._audio_path is None
|
|
134
|
+
) and not self._is_audio_external else
|
|
104
135
|
AudioStream(
|
|
105
136
|
MediaSource.DEVICE,
|
|
106
137
|
self._audio_path,
|
|
107
138
|
self._audio_parameters,
|
|
108
139
|
)
|
|
109
140
|
if self._is_audio_device else
|
|
141
|
+
AudioStream(
|
|
142
|
+
MediaSource.EXTERNAL,
|
|
143
|
+
'',
|
|
144
|
+
self._audio_parameters,
|
|
145
|
+
)
|
|
146
|
+
if self._is_audio_external else
|
|
110
147
|
AudioStream(
|
|
111
148
|
MediaSource.SHELL,
|
|
112
149
|
' '.join(
|
|
@@ -123,7 +160,10 @@ class MediaStream(Stream):
|
|
|
123
160
|
self._audio_parameters,
|
|
124
161
|
),
|
|
125
162
|
camera=None
|
|
126
|
-
if
|
|
163
|
+
if (
|
|
164
|
+
self._video_flags & MediaStream.Flags.IGNORE or
|
|
165
|
+
self._media_path is None
|
|
166
|
+
) and not self._is_video_external else
|
|
127
167
|
VideoStream(
|
|
128
168
|
MediaSource.DESKTOP if isinstance(media_path, ScreenDevice)
|
|
129
169
|
else MediaSource.DEVICE,
|
|
@@ -131,6 +171,12 @@ class MediaStream(Stream):
|
|
|
131
171
|
self._video_parameters,
|
|
132
172
|
)
|
|
133
173
|
if self._is_media_device else
|
|
174
|
+
VideoStream(
|
|
175
|
+
MediaSource.EXTERNAL,
|
|
176
|
+
'',
|
|
177
|
+
self._video_parameters,
|
|
178
|
+
)
|
|
179
|
+
if self._is_video_external else
|
|
134
180
|
VideoStream(
|
|
135
181
|
MediaSource.SHELL,
|
|
136
182
|
' '.join(
|
|
@@ -149,7 +195,8 @@ class MediaStream(Stream):
|
|
|
149
195
|
)
|
|
150
196
|
|
|
151
197
|
async def check_stream(self):
|
|
152
|
-
if not self._video_flags & MediaStream.Flags.IGNORE
|
|
198
|
+
if not self._video_flags & MediaStream.Flags.IGNORE and \
|
|
199
|
+
not self._is_video_external:
|
|
153
200
|
if self._is_media_device:
|
|
154
201
|
if not self._media_path:
|
|
155
202
|
self.camera = None
|
|
@@ -205,7 +252,8 @@ class MediaStream(Stream):
|
|
|
205
252
|
self._audio_path = self._audio_path \
|
|
206
253
|
if self._audio_path else self._media_path
|
|
207
254
|
|
|
208
|
-
if not self._audio_flags & MediaStream.Flags.IGNORE
|
|
255
|
+
if not self._audio_flags & MediaStream.Flags.IGNORE and \
|
|
256
|
+
not self._is_audio_external:
|
|
209
257
|
if self._is_audio_device:
|
|
210
258
|
if not self._audio_path:
|
|
211
259
|
self.microphone = None
|
|
@@ -8,10 +8,10 @@ class StreamFrame(Update):
|
|
|
8
8
|
class Info:
|
|
9
9
|
def __init__(
|
|
10
10
|
self,
|
|
11
|
-
capture_time: int,
|
|
12
|
-
width: int,
|
|
13
|
-
height: int,
|
|
14
|
-
rotation: int,
|
|
11
|
+
capture_time: int = 0,
|
|
12
|
+
width: int = 0,
|
|
13
|
+
height: int = 0,
|
|
14
|
+
rotation: int = 0,
|
|
15
15
|
):
|
|
16
16
|
self.capture_time = capture_time
|
|
17
17
|
self.width = width
|
|
File without changes
|
|
File without changes
|
|
File without changes
|