py-tgcalls 2.1.0.dev4__py3-none-any.whl → 2.1.0.dev6__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.dev4.dist-info → py_tgcalls-2.1.0.dev6.dist-info}/METADATA +2 -2
- {py_tgcalls-2.1.0.dev4.dist-info → py_tgcalls-2.1.0.dev6.dist-info}/RECORD +26 -23
- pytgcalls/__version__.py +1 -1
- pytgcalls/ffmpeg.py +2 -2
- pytgcalls/filters.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 +22 -19
- 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 +6 -2
- pytgcalls/types/stream/__init__.py +6 -2
- pytgcalls/types/stream/device.py +12 -0
- pytgcalls/types/stream/external_media.py +8 -0
- pytgcalls/types/stream/frame.py +26 -0
- pytgcalls/types/stream/media_stream.py +54 -6
- pytgcalls/types/stream/stream_frames.py +20 -0
- pytgcalls/types/stream/stream_frame.py +0 -35
- {py_tgcalls-2.1.0.dev4.dist-info → py_tgcalls-2.1.0.dev6.dist-info}/LICENSE +0 -0
- {py_tgcalls-2.1.0.dev4.dist-info → py_tgcalls-2.1.0.dev6.dist-info}/WHEEL +0 -0
- {py_tgcalls-2.1.0.dev4.dist-info → py_tgcalls-2.1.0.dev6.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.dev6
|
|
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.0b8
|
|
191
191
|
Requires-Dist: deprecation
|
|
192
192
|
Provides-Extra: pyrogram
|
|
193
193
|
Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
|
|
2
|
-
pytgcalls/__version__.py,sha256=
|
|
2
|
+
pytgcalls/__version__.py,sha256=XpaslcQ6vxyxgkMYX7CybYFwTHghQiu_sJjTLT479EA,27
|
|
3
3
|
pytgcalls/environment.py,sha256=ctCHACvG6l8SdpPewSBhOvc70kbwpv18maC0TwLvZ08,1924
|
|
4
4
|
pytgcalls/exceptions.py,sha256=0MmAktc53ajYAc7ThjD2tJ9PDyibUi0iHZMfUy2IoKs,4109
|
|
5
|
-
pytgcalls/ffmpeg.py,sha256=
|
|
6
|
-
pytgcalls/filters.py,sha256=
|
|
5
|
+
pytgcalls/ffmpeg.py,sha256=tm6DBxyNfPh3h3an-b2s9x1UyX-cvkCdov9prlXxVZY,8649
|
|
6
|
+
pytgcalls/filters.py,sha256=_TodZPFizBpH5ASGpiU92UBG2t4KUKyKDS2Dff03Dto,6041
|
|
7
7
|
pytgcalls/mtproto_required.py,sha256=6B-31p5qH_6oekUgypV4nK3hqPS6Nr-pA8S81wjnbaY,630
|
|
8
8
|
pytgcalls/mutex.py,sha256=Frjji5Ctzlk4AXEBuBLnDK-7HbtreoV6zuyKpFpMNI4,236
|
|
9
9
|
pytgcalls/pytgcalls.py,sha256=oBcWgBwusnXmjHrLEE99VVXARReVyrXdn9SyeBWHbVo,1479
|
|
@@ -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=Kj9R8OqUM-g7pt-FiWP-US7sCFkH5ciPr9S8v-WPtLg,1062
|
|
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=tnNMKflSfQecORN4tBYhyDQMctWXpXidj7z1xsfUQb8,13517
|
|
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=iXAzXG5WgbICLQV1JT_F4QtOqKWC8X3334-MOv2SXW4,1127
|
|
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,19 @@ 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=4CfgKXoEZX8BvV_ImBlY108MvRELNCf_-CPAMJ9szgk,561
|
|
87
88
|
pytgcalls/types/stream/audio_quality.py,sha256=4X94ErmTeLP4TVcE3eLtPPdtluSPxgxbgTosuNJOVhc,141
|
|
88
|
-
pytgcalls/types/stream/device.py,sha256=
|
|
89
|
+
pytgcalls/types/stream/device.py,sha256=EdoDg6lPE7fgoZI04Nr0E9zbIk-iRIBgYYAzVqoCBPM,961
|
|
89
90
|
pytgcalls/types/stream/direction.py,sha256=gd10wUmpfsqx87kCAPZt6u8pFiPb09WZfHKcMWAZokU,394
|
|
90
|
-
pytgcalls/types/stream/
|
|
91
|
+
pytgcalls/types/stream/external_media.py,sha256=RiuSX5tZGdNsQZ8LIRk5Lp4Ksv9oTvaccmInJRZYo4M,114
|
|
92
|
+
pytgcalls/types/stream/frame.py,sha256=TXo5HZVHbbaVNBqulMhTqGODXH3bpBVlN_of1rosNUQ,586
|
|
93
|
+
pytgcalls/types/stream/media_stream.py,sha256=sFlP8BMmCjhYp_A8U7XkGV4jbxR7PYsmJiII5Fxs0_A,11971
|
|
91
94
|
pytgcalls/types/stream/record_stream.py,sha256=pp08OahflXJ5Cs281KxU-K2ULRP88ega5WBkJ7dwBjk,3038
|
|
92
95
|
pytgcalls/types/stream/stream_ended.py,sha256=xR_kZwFf03hA6rw_nvI7Be7GwoCKzQf_1MKaGpPDXqY,716
|
|
93
|
-
pytgcalls/types/stream/
|
|
96
|
+
pytgcalls/types/stream/stream_frames.py,sha256=028ZhNV-mN3BGqMlmxusAV1xDQpXRYCeM0WXBZhRUhA,446
|
|
94
97
|
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.
|
|
98
|
+
py_tgcalls-2.1.0.dev6.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
99
|
+
py_tgcalls-2.1.0.dev6.dist-info/METADATA,sha256=8KhT3gZqD-Ube9Drf9oxvhsk7Nnp4KMe7QPYHGPdCw0,14399
|
|
100
|
+
py_tgcalls-2.1.0.dev6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
101
|
+
py_tgcalls-2.1.0.dev6.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
|
|
102
|
+
py_tgcalls-2.1.0.dev6.dist-info/RECORD,,
|
pytgcalls/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '2.1.0.
|
|
1
|
+
__version__ = '2.1.0.dev6'
|
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']
|
pytgcalls/filters.py
CHANGED
|
@@ -11,7 +11,7 @@ from .types import Device
|
|
|
11
11
|
from .types import Direction
|
|
12
12
|
from .types import GroupCallParticipant
|
|
13
13
|
from .types import StreamEnded
|
|
14
|
-
from .types import
|
|
14
|
+
from .types import StreamFrames
|
|
15
15
|
from .types import Update
|
|
16
16
|
from .types import UpdatedGroupCallParticipant
|
|
17
17
|
|
|
@@ -209,7 +209,7 @@ class stream_frame(Filter):
|
|
|
209
209
|
self.devices = devices
|
|
210
210
|
|
|
211
211
|
async def __call__(self, client: PyTgCalls, update: Update):
|
|
212
|
-
if isinstance(update,
|
|
212
|
+
if isinstance(update, StreamFrames):
|
|
213
213
|
return (
|
|
214
214
|
(
|
|
215
215
|
self.directions is None or
|
|
@@ -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 Frame
|
|
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: Frame.Info = Frame.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()
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
from typing import List
|
|
3
4
|
|
|
4
5
|
from ntgcalls import CallNetworkState
|
|
5
6
|
from ntgcalls import ConnectionError
|
|
6
7
|
from ntgcalls import ConnectionNotFound
|
|
7
8
|
from ntgcalls import ConnectionState
|
|
8
|
-
from ntgcalls import
|
|
9
|
+
from ntgcalls import Frame as RawFrame
|
|
9
10
|
from ntgcalls import MediaState
|
|
10
11
|
from ntgcalls import StreamDevice
|
|
11
12
|
from ntgcalls import StreamMode
|
|
@@ -22,10 +23,11 @@ from ...types import CallData
|
|
|
22
23
|
from ...types import ChatUpdate
|
|
23
24
|
from ...types import Device
|
|
24
25
|
from ...types import Direction
|
|
26
|
+
from ...types import Frame
|
|
25
27
|
from ...types import GroupCallParticipant
|
|
26
28
|
from ...types import RawCallUpdate
|
|
27
29
|
from ...types import StreamEnded
|
|
28
|
-
from ...types import
|
|
30
|
+
from ...types import StreamFrames
|
|
29
31
|
from ...types import Update
|
|
30
32
|
from ...types import UpdatedGroupCallParticipant
|
|
31
33
|
|
|
@@ -49,6 +51,7 @@ class Start(Scaffold):
|
|
|
49
51
|
if isinstance(update, ChatUpdate) and \
|
|
50
52
|
p2p_config.outgoing:
|
|
51
53
|
if update.status & ChatUpdate.Status.DISCARDED_CALL:
|
|
54
|
+
self._wait_connect.pop(chat_id, None)
|
|
52
55
|
p2p_config.wait_data.set_exception(
|
|
53
56
|
CallDeclined(
|
|
54
57
|
chat_id,
|
|
@@ -228,25 +231,27 @@ class Start(Scaffold):
|
|
|
228
231
|
|
|
229
232
|
async def stream_frame(
|
|
230
233
|
chat_id: int,
|
|
231
|
-
source_id: int,
|
|
232
234
|
mode: StreamMode,
|
|
233
235
|
device: StreamDevice,
|
|
234
|
-
|
|
235
|
-
frame_info: FrameData,
|
|
236
|
+
frames: List[RawFrame],
|
|
236
237
|
):
|
|
237
238
|
await self.propagate(
|
|
238
|
-
|
|
239
|
+
StreamFrames(
|
|
239
240
|
chat_id,
|
|
240
|
-
source_id,
|
|
241
241
|
Direction.from_raw(mode),
|
|
242
242
|
Device.from_raw(device),
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
243
|
+
[
|
|
244
|
+
Frame(
|
|
245
|
+
x.ssrc,
|
|
246
|
+
x.data,
|
|
247
|
+
Frame.Info(
|
|
248
|
+
x.frame_data.absolute_capture_timestamp_ms,
|
|
249
|
+
x.frame_data.width,
|
|
250
|
+
x.frame_data.height,
|
|
251
|
+
x.frame_data.rotation,
|
|
252
|
+
),
|
|
253
|
+
) for x in frames
|
|
254
|
+
],
|
|
250
255
|
),
|
|
251
256
|
self,
|
|
252
257
|
)
|
|
@@ -323,16 +328,14 @@ class Start(Scaffold):
|
|
|
323
328
|
self.loop,
|
|
324
329
|
),
|
|
325
330
|
)
|
|
326
|
-
self._binding.
|
|
327
|
-
lambda chat_id,
|
|
331
|
+
self._binding.on_frames(
|
|
332
|
+
lambda chat_id, mode, device, frames:
|
|
328
333
|
asyncio.run_coroutine_threadsafe(
|
|
329
334
|
stream_frame(
|
|
330
335
|
chat_id,
|
|
331
|
-
source_id,
|
|
332
336
|
mode,
|
|
333
337
|
device,
|
|
334
|
-
|
|
335
|
-
info,
|
|
338
|
+
frames,
|
|
336
339
|
),
|
|
337
340
|
self.loop,
|
|
338
341
|
),
|
|
@@ -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,10 +12,12 @@ 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
|
|
16
|
+
from .stream import Frame
|
|
15
17
|
from .stream import MediaStream
|
|
16
18
|
from .stream import RecordStream
|
|
17
19
|
from .stream import StreamEnded
|
|
18
|
-
from .stream import
|
|
20
|
+
from .stream import StreamFrames
|
|
19
21
|
from .stream import VideoQuality
|
|
20
22
|
from .update import Update
|
|
21
23
|
|
|
@@ -23,6 +25,8 @@ __all__ = (
|
|
|
23
25
|
'AudioQuality',
|
|
24
26
|
'Device',
|
|
25
27
|
'Direction',
|
|
28
|
+
'ExternalMedia',
|
|
29
|
+
'Frame',
|
|
26
30
|
'Browsers',
|
|
27
31
|
'Cache',
|
|
28
32
|
'ChatUpdate',
|
|
@@ -36,7 +40,7 @@ __all__ = (
|
|
|
36
40
|
'RecordStream',
|
|
37
41
|
'MediaStream',
|
|
38
42
|
'StreamEnded',
|
|
39
|
-
'
|
|
43
|
+
'StreamFrames',
|
|
40
44
|
'Update',
|
|
41
45
|
'UpdatedGroupCallParticipant',
|
|
42
46
|
'VideoQuality',
|
|
@@ -1,19 +1,23 @@
|
|
|
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
|
|
5
|
+
from .frame import Frame
|
|
4
6
|
from .media_stream import MediaStream
|
|
5
7
|
from .record_stream import RecordStream
|
|
6
8
|
from .stream_ended import StreamEnded
|
|
7
|
-
from .
|
|
9
|
+
from .stream_frames import StreamFrames
|
|
8
10
|
from .video_quality import VideoQuality
|
|
9
11
|
|
|
10
12
|
__all__ = (
|
|
11
13
|
'AudioQuality',
|
|
12
14
|
'Device',
|
|
13
15
|
'Direction',
|
|
16
|
+
'ExternalMedia',
|
|
17
|
+
'Frame',
|
|
14
18
|
'MediaStream',
|
|
15
19
|
'RecordStream',
|
|
16
20
|
'StreamEnded',
|
|
17
|
-
'
|
|
21
|
+
'StreamFrames',
|
|
18
22
|
'VideoQuality',
|
|
19
23
|
)
|
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
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ...types.update import PyObject
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Frame(PyObject):
|
|
5
|
+
class Info(PyObject):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
capture_time: int = 0,
|
|
9
|
+
width: int = 0,
|
|
10
|
+
height: int = 0,
|
|
11
|
+
rotation: int = 0,
|
|
12
|
+
):
|
|
13
|
+
self.capture_time = capture_time
|
|
14
|
+
self.width = width
|
|
15
|
+
self.height = height
|
|
16
|
+
self.rotation = rotation
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
ssrc: int,
|
|
21
|
+
frame: bytes,
|
|
22
|
+
info: Info,
|
|
23
|
+
):
|
|
24
|
+
self.ssrc = ssrc
|
|
25
|
+
self.frame = frame
|
|
26
|
+
self.info = info
|
|
@@ -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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from ...types.update import Update
|
|
4
|
+
from .device import Device
|
|
5
|
+
from .direction import Direction
|
|
6
|
+
from .frame import Frame
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StreamFrames(Update):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
chat_id: int,
|
|
13
|
+
direction: Direction,
|
|
14
|
+
device: Device,
|
|
15
|
+
frames: List[Frame],
|
|
16
|
+
):
|
|
17
|
+
super().__init__(chat_id)
|
|
18
|
+
self.direction = direction
|
|
19
|
+
self.device = device
|
|
20
|
+
self.frames = frames
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from ...types.update import Update
|
|
2
|
-
from .device import Device
|
|
3
|
-
from .direction import Direction
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class StreamFrame(Update):
|
|
7
|
-
|
|
8
|
-
class Info:
|
|
9
|
-
def __init__(
|
|
10
|
-
self,
|
|
11
|
-
capture_time: int,
|
|
12
|
-
width: int,
|
|
13
|
-
height: int,
|
|
14
|
-
rotation: int,
|
|
15
|
-
):
|
|
16
|
-
self.capture_time = capture_time
|
|
17
|
-
self.width = width
|
|
18
|
-
self.height = height
|
|
19
|
-
self.rotation = rotation
|
|
20
|
-
|
|
21
|
-
def __init__(
|
|
22
|
-
self,
|
|
23
|
-
chat_id: int,
|
|
24
|
-
ssrc: int,
|
|
25
|
-
direction: Direction,
|
|
26
|
-
device: Device,
|
|
27
|
-
frame: bytes,
|
|
28
|
-
info: Info,
|
|
29
|
-
):
|
|
30
|
-
super().__init__(chat_id)
|
|
31
|
-
self.ssrc = ssrc
|
|
32
|
-
self.direction = direction
|
|
33
|
-
self.device = device
|
|
34
|
-
self.frame = frame
|
|
35
|
-
self.info = info
|
|
File without changes
|
|
File without changes
|
|
File without changes
|