py-tgcalls 2.0.6__py3-none-any.whl → 2.1.0.dev2__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.0.6.dist-info → py_tgcalls-2.1.0.dev2.dist-info}/METADATA +6 -8
- {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dev2.dist-info}/RECORD +41 -35
- {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dev2.dist-info}/WHEEL +1 -1
- pytgcalls/__init__.py +2 -0
- pytgcalls/__version__.py +1 -1
- pytgcalls/filters.py +46 -6
- pytgcalls/media_devices/__init__.py +6 -2
- pytgcalls/media_devices/device_info.py +8 -15
- pytgcalls/media_devices/input_device.py +11 -0
- pytgcalls/media_devices/media_devices.py +41 -92
- pytgcalls/media_devices/screen_device.py +10 -0
- pytgcalls/media_devices/speaker_device.py +10 -0
- pytgcalls/methods/stream/__init__.py +4 -2
- pytgcalls/methods/stream/play.py +73 -11
- pytgcalls/methods/stream/record.py +41 -0
- pytgcalls/methods/stream/{played_time.py → time.py} +5 -3
- pytgcalls/methods/utilities/call_holder.py +5 -2
- pytgcalls/methods/utilities/start.py +118 -13
- pytgcalls/methods/utilities/stream_params.py +63 -22
- pytgcalls/mtproto/bridged_client.py +34 -2
- pytgcalls/mtproto/client_cache.py +46 -16
- pytgcalls/mtproto/hydrogram_client.py +53 -11
- pytgcalls/mtproto/mtproto_client.py +30 -4
- pytgcalls/mtproto/pyrogram_client.py +53 -11
- pytgcalls/mtproto/telethon_client.py +61 -19
- pytgcalls/scaffold.py +6 -0
- pytgcalls/types/__init__.py +10 -4
- pytgcalls/types/calls/call.py +5 -3
- pytgcalls/types/chats/group_call_participant.py +21 -0
- pytgcalls/types/raw/audio_stream.py +3 -3
- pytgcalls/types/raw/stream.py +8 -4
- pytgcalls/types/raw/video_stream.py +5 -4
- pytgcalls/types/stream/__init__.py +10 -4
- pytgcalls/types/stream/device.py +24 -0
- pytgcalls/types/stream/direction.py +18 -0
- pytgcalls/types/stream/media_stream.py +126 -102
- pytgcalls/types/stream/record_stream.py +93 -0
- pytgcalls/types/stream/stream_ended.py +32 -0
- pytgcalls/types/stream/stream_frame.py +35 -0
- pytgcalls/media_devices/screen_info.py +0 -45
- pytgcalls/types/stream/stream_audio_ended.py +0 -9
- pytgcalls/types/stream/stream_video_ended.py +0 -9
- {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dev2.dist-info}/LICENSE +0 -0
- {py_tgcalls-2.0.6.dist-info → py_tgcalls-2.1.0.dev2.dist-info}/top_level.txt +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
from ntgcalls import
|
|
1
|
+
from ntgcalls import MediaSource
|
|
2
2
|
|
|
3
3
|
from ...statictypes import statictypes
|
|
4
|
+
from ..py_object import PyObject
|
|
4
5
|
from .video_parameters import VideoParameters
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
class VideoStream:
|
|
8
|
+
class VideoStream(PyObject):
|
|
8
9
|
@statictypes
|
|
9
10
|
def __init__(
|
|
10
11
|
self,
|
|
11
|
-
|
|
12
|
+
media_source: MediaSource,
|
|
12
13
|
path: str,
|
|
13
14
|
parameters: VideoParameters = VideoParameters(),
|
|
14
15
|
):
|
|
15
|
-
self.
|
|
16
|
+
self.media_source: MediaSource = media_source
|
|
16
17
|
self.path: str = path
|
|
17
18
|
self.parameters: VideoParameters = parameters
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
from .audio_quality import AudioQuality
|
|
2
|
+
from .device import Device
|
|
3
|
+
from .direction import Direction
|
|
2
4
|
from .media_stream import MediaStream
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
5
|
+
from .record_stream import RecordStream
|
|
6
|
+
from .stream_ended import StreamEnded
|
|
7
|
+
from .stream_frame import StreamFrame
|
|
5
8
|
from .video_quality import VideoQuality
|
|
6
9
|
|
|
7
10
|
__all__ = (
|
|
8
11
|
'AudioQuality',
|
|
12
|
+
'Device',
|
|
13
|
+
'Direction',
|
|
9
14
|
'MediaStream',
|
|
10
|
-
'
|
|
11
|
-
'
|
|
15
|
+
'RecordStream',
|
|
16
|
+
'StreamEnded',
|
|
17
|
+
'StreamFrame',
|
|
12
18
|
'VideoQuality',
|
|
13
19
|
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from enum import auto
|
|
2
|
+
|
|
3
|
+
from ntgcalls import StreamDevice
|
|
4
|
+
|
|
5
|
+
from ..flag import Flag
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Device(Flag):
|
|
9
|
+
MICROPHONE = auto()
|
|
10
|
+
SPEAKER = auto()
|
|
11
|
+
CAMERA = auto()
|
|
12
|
+
SCREEN = auto()
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def from_raw(device: StreamDevice):
|
|
16
|
+
if device == StreamDevice.MICROPHONE:
|
|
17
|
+
return Device.MICROPHONE
|
|
18
|
+
if device == StreamDevice.SPEAKER:
|
|
19
|
+
return Device.SPEAKER
|
|
20
|
+
if device == StreamDevice.CAMERA:
|
|
21
|
+
return Device.CAMERA
|
|
22
|
+
if device == StreamDevice.SCREEN:
|
|
23
|
+
return Device.SCREEN
|
|
24
|
+
return None
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from enum import auto
|
|
2
|
+
|
|
3
|
+
from ntgcalls import StreamMode
|
|
4
|
+
|
|
5
|
+
from ..flag import Flag
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Direction(Flag):
|
|
9
|
+
OUTGOING = auto()
|
|
10
|
+
INCOMING = auto()
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def from_raw(direction: StreamMode):
|
|
14
|
+
if direction == StreamMode.OUTGOING:
|
|
15
|
+
return Direction.OUTGOING
|
|
16
|
+
if direction == StreamMode.INCOMING:
|
|
17
|
+
return Direction.INCOMING
|
|
18
|
+
return None
|
|
@@ -4,7 +4,7 @@ from typing import Dict
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
|
-
from ntgcalls import
|
|
7
|
+
from ntgcalls import MediaSource
|
|
8
8
|
|
|
9
9
|
from ...exceptions import ImageSourceFound
|
|
10
10
|
from ...exceptions import LiveStreamFound
|
|
@@ -12,8 +12,8 @@ from ...exceptions import NoAudioSourceFound
|
|
|
12
12
|
from ...exceptions import NoVideoSourceFound
|
|
13
13
|
from ...ffmpeg import build_command
|
|
14
14
|
from ...ffmpeg import check_stream
|
|
15
|
-
from ...media_devices import
|
|
16
|
-
from ...media_devices import
|
|
15
|
+
from ...media_devices.input_device import InputDevice
|
|
16
|
+
from ...media_devices.screen_device import ScreenDevice
|
|
17
17
|
from ...statictypes import statictypes
|
|
18
18
|
from ...ytdlp import YtDlp
|
|
19
19
|
from ..flag import Flag
|
|
@@ -31,12 +31,11 @@ class MediaStream(Stream):
|
|
|
31
31
|
AUTO_DETECT = auto()
|
|
32
32
|
REQUIRED = auto()
|
|
33
33
|
IGNORE = auto()
|
|
34
|
-
NO_LATENCY = auto()
|
|
35
34
|
|
|
36
35
|
@statictypes
|
|
37
36
|
def __init__(
|
|
38
37
|
self,
|
|
39
|
-
media_path: Union[str, Path,
|
|
38
|
+
media_path: Union[str, Path, InputDevice],
|
|
40
39
|
audio_parameters: Union[
|
|
41
40
|
AudioParameters,
|
|
42
41
|
AudioQuality,
|
|
@@ -45,7 +44,7 @@ class MediaStream(Stream):
|
|
|
45
44
|
VideoParameters,
|
|
46
45
|
VideoQuality,
|
|
47
46
|
] = VideoQuality.SD_480p,
|
|
48
|
-
audio_path: Optional[Union[str, Path,
|
|
47
|
+
audio_path: Optional[Union[str, Path, InputDevice]] = None,
|
|
49
48
|
audio_flags: Optional[Flags] = Flags.AUTO_DETECT,
|
|
50
49
|
video_flags: Optional[Flags] = Flags.AUTO_DETECT,
|
|
51
50
|
headers: Optional[Dict[str, str]] = None,
|
|
@@ -69,35 +68,47 @@ class MediaStream(Stream):
|
|
|
69
68
|
|
|
70
69
|
self._media_path: Optional[str] = None
|
|
71
70
|
self._audio_path: Optional[str] = None
|
|
71
|
+
self._is_media_device: bool = False
|
|
72
|
+
self._is_audio_device: bool = False
|
|
72
73
|
if isinstance(media_path, str):
|
|
73
74
|
self._media_path = media_path
|
|
74
75
|
elif isinstance(media_path, Path):
|
|
75
76
|
self._media_path = str(media_path)
|
|
76
|
-
elif isinstance(media_path,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
self.
|
|
81
|
-
|
|
77
|
+
elif isinstance(media_path, (InputDevice, ScreenDevice)):
|
|
78
|
+
print('MediaStream', media_path.is_video)
|
|
79
|
+
if media_path.is_video:
|
|
80
|
+
self._media_path = media_path.metadata
|
|
81
|
+
self._is_media_device = True
|
|
82
|
+
else:
|
|
83
|
+
self._audio_path = media_path.metadata
|
|
84
|
+
self._is_audio_device = True
|
|
82
85
|
|
|
83
86
|
if isinstance(audio_path, str):
|
|
84
87
|
self._audio_path = audio_path
|
|
85
88
|
elif isinstance(audio_path, Path):
|
|
86
89
|
self._audio_path = str(audio_path)
|
|
87
|
-
elif isinstance(audio_path,
|
|
88
|
-
|
|
90
|
+
elif isinstance(audio_path, (InputDevice, ScreenDevice)):
|
|
91
|
+
if audio_path.is_video:
|
|
92
|
+
raise ValueError('Audio path must be an audio device')
|
|
93
|
+
self._audio_path = audio_path.metadata
|
|
94
|
+
self._is_audio_device = True
|
|
89
95
|
|
|
90
96
|
self._audio_flags = self._filter_flags(audio_flags)
|
|
91
97
|
self._video_flags = self._filter_flags(video_flags)
|
|
92
98
|
self._ffmpeg_parameters = ffmpeg_parameters
|
|
93
99
|
self._ytdlp_parameters = ytdlp_parameters
|
|
94
100
|
self._headers = headers
|
|
95
|
-
|
|
96
101
|
super().__init__(
|
|
97
|
-
|
|
102
|
+
microphone=None
|
|
98
103
|
if self._audio_flags & MediaStream.Flags.IGNORE else
|
|
99
104
|
AudioStream(
|
|
100
|
-
|
|
105
|
+
MediaSource.DEVICE,
|
|
106
|
+
self._audio_path,
|
|
107
|
+
self._audio_parameters,
|
|
108
|
+
)
|
|
109
|
+
if self._is_audio_device else
|
|
110
|
+
AudioStream(
|
|
111
|
+
MediaSource.SHELL,
|
|
101
112
|
' '.join(
|
|
102
113
|
build_command(
|
|
103
114
|
'ffmpeg',
|
|
@@ -111,10 +122,17 @@ class MediaStream(Stream):
|
|
|
111
122
|
),
|
|
112
123
|
self._audio_parameters,
|
|
113
124
|
),
|
|
114
|
-
|
|
125
|
+
camera=None
|
|
115
126
|
if self._video_flags & MediaStream.Flags.IGNORE else
|
|
116
127
|
VideoStream(
|
|
117
|
-
|
|
128
|
+
MediaSource.DESKTOP if isinstance(media_path, ScreenDevice)
|
|
129
|
+
else MediaSource.DEVICE,
|
|
130
|
+
self._media_path,
|
|
131
|
+
self._video_parameters,
|
|
132
|
+
)
|
|
133
|
+
if self._is_media_device else
|
|
134
|
+
VideoStream(
|
|
135
|
+
MediaSource.SHELL,
|
|
118
136
|
' '.join(
|
|
119
137
|
build_command(
|
|
120
138
|
'ffmpeg',
|
|
@@ -132,91 +150,104 @@ class MediaStream(Stream):
|
|
|
132
150
|
|
|
133
151
|
async def check_stream(self):
|
|
134
152
|
if not self._video_flags & MediaStream.Flags.IGNORE:
|
|
135
|
-
if
|
|
136
|
-
|
|
137
|
-
self.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
self._media_path = links[0]
|
|
142
|
-
if not self._audio_path:
|
|
143
|
-
self._audio_path = links[1]
|
|
144
|
-
try:
|
|
145
|
-
image_commands = []
|
|
146
|
-
live_stream = False
|
|
147
|
-
try:
|
|
148
|
-
await check_stream(
|
|
149
|
-
self._ffmpeg_parameters,
|
|
153
|
+
if self._is_media_device:
|
|
154
|
+
if not self._media_path:
|
|
155
|
+
self.camera = None
|
|
156
|
+
elif self._media_path:
|
|
157
|
+
if YtDlp.is_valid(self._media_path):
|
|
158
|
+
links = await YtDlp.extract(
|
|
150
159
|
self._media_path,
|
|
151
160
|
self._video_parameters,
|
|
152
|
-
|
|
153
|
-
self._headers,
|
|
161
|
+
self._ytdlp_parameters,
|
|
154
162
|
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
image_commands
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
163
|
+
self._media_path = links[0]
|
|
164
|
+
if not self._audio_path:
|
|
165
|
+
self._audio_path = links[1]
|
|
166
|
+
try:
|
|
167
|
+
image_commands = []
|
|
168
|
+
live_stream = False
|
|
169
|
+
try:
|
|
170
|
+
await check_stream(
|
|
171
|
+
self._ffmpeg_parameters,
|
|
172
|
+
self._media_path,
|
|
173
|
+
self._video_parameters,
|
|
174
|
+
[],
|
|
175
|
+
self._headers,
|
|
176
|
+
)
|
|
177
|
+
except ImageSourceFound:
|
|
178
|
+
image_commands = [
|
|
179
|
+
'-loop',
|
|
180
|
+
'1',
|
|
181
|
+
'-framerate',
|
|
182
|
+
'1',
|
|
183
|
+
]
|
|
184
|
+
except LiveStreamFound:
|
|
185
|
+
live_stream = True
|
|
186
|
+
self.camera.path = ' '.join(
|
|
187
|
+
build_command(
|
|
188
|
+
'ffmpeg',
|
|
189
|
+
self._ffmpeg_parameters,
|
|
190
|
+
self._media_path,
|
|
191
|
+
self._video_parameters,
|
|
192
|
+
image_commands,
|
|
193
|
+
self._headers,
|
|
194
|
+
live_stream,
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
except NoVideoSourceFound as e:
|
|
198
|
+
if self._video_flags & MediaStream.Flags.REQUIRED:
|
|
199
|
+
raise e
|
|
200
|
+
self.camera = None
|
|
201
|
+
else:
|
|
202
|
+
self.camera = None
|
|
179
203
|
|
|
180
|
-
|
|
181
|
-
|
|
204
|
+
if not self._is_media_device:
|
|
205
|
+
self._audio_path = self._audio_path \
|
|
206
|
+
if self._audio_path else self._media_path
|
|
182
207
|
|
|
183
208
|
if not self._audio_flags & MediaStream.Flags.IGNORE:
|
|
184
|
-
if
|
|
185
|
-
self._audio_path
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
209
|
+
if self._is_audio_device:
|
|
210
|
+
if not self._audio_path:
|
|
211
|
+
self.microphone = None
|
|
212
|
+
elif self._audio_path:
|
|
213
|
+
if YtDlp.is_valid(self._audio_path):
|
|
214
|
+
self._audio_path = (
|
|
215
|
+
await YtDlp.extract(
|
|
216
|
+
self._audio_path,
|
|
217
|
+
self._video_parameters,
|
|
218
|
+
self._ytdlp_parameters,
|
|
219
|
+
)
|
|
220
|
+
)[1]
|
|
192
221
|
|
|
193
|
-
try:
|
|
194
|
-
live_stream = False
|
|
195
222
|
try:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
223
|
+
live_stream = False
|
|
224
|
+
try:
|
|
225
|
+
await check_stream(
|
|
226
|
+
self._ffmpeg_parameters,
|
|
227
|
+
self._audio_path,
|
|
228
|
+
self._audio_parameters,
|
|
229
|
+
[],
|
|
230
|
+
self._headers,
|
|
231
|
+
)
|
|
232
|
+
except LiveStreamFound:
|
|
233
|
+
live_stream = True
|
|
234
|
+
self.microphone.path = ' '.join(
|
|
235
|
+
build_command(
|
|
236
|
+
'ffmpeg',
|
|
237
|
+
self._ffmpeg_parameters,
|
|
238
|
+
self._audio_path,
|
|
239
|
+
self._audio_parameters,
|
|
240
|
+
[],
|
|
241
|
+
self._headers,
|
|
242
|
+
live_stream,
|
|
243
|
+
),
|
|
202
244
|
)
|
|
203
|
-
except
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
self._audio_path,
|
|
210
|
-
self._audio_parameters,
|
|
211
|
-
[],
|
|
212
|
-
self._headers,
|
|
213
|
-
live_stream,
|
|
214
|
-
),
|
|
215
|
-
)
|
|
216
|
-
except NoAudioSourceFound as e:
|
|
217
|
-
if self._audio_flags & MediaStream.Flags.REQUIRED:
|
|
218
|
-
raise e
|
|
219
|
-
self.stream_audio = None
|
|
245
|
+
except NoAudioSourceFound as e:
|
|
246
|
+
if self._audio_flags & MediaStream.Flags.REQUIRED:
|
|
247
|
+
raise e
|
|
248
|
+
self.microphone = None
|
|
249
|
+
else:
|
|
250
|
+
self.microphone = None
|
|
220
251
|
|
|
221
252
|
@staticmethod
|
|
222
253
|
def _filter_flags(flags: Optional[Flags]) -> Flags:
|
|
@@ -236,10 +267,3 @@ class MediaStream(Stream):
|
|
|
236
267
|
key=lambda flag: flag.value,
|
|
237
268
|
)
|
|
238
269
|
return flags & ~combined_flags_value | potential_flag
|
|
239
|
-
|
|
240
|
-
@staticmethod
|
|
241
|
-
def _flags(flags: Flags) -> InputMode:
|
|
242
|
-
new_flags = InputMode.SHELL
|
|
243
|
-
if flags & MediaStream.Flags.NO_LATENCY:
|
|
244
|
-
new_flags |= InputMode.NO_LATENCY
|
|
245
|
-
return new_flags
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from ntgcalls import MediaSource
|
|
4
|
+
|
|
5
|
+
from ...media_devices.speaker_device import SpeakerDevice
|
|
6
|
+
from ...statictypes import statictypes
|
|
7
|
+
from ..raw.audio_parameters import AudioParameters
|
|
8
|
+
from ..raw.audio_stream import AudioStream
|
|
9
|
+
from ..raw.stream import Stream
|
|
10
|
+
from ..raw.video_parameters import VideoParameters
|
|
11
|
+
from ..raw.video_stream import VideoStream
|
|
12
|
+
from ..stream.audio_quality import AudioQuality
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RecordStream(Stream):
|
|
16
|
+
@statictypes
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
audio: Union[bool, str, SpeakerDevice] = False,
|
|
20
|
+
audio_parameters: Union[
|
|
21
|
+
AudioParameters,
|
|
22
|
+
AudioQuality,
|
|
23
|
+
] = AudioQuality.HIGH,
|
|
24
|
+
camera: bool = False,
|
|
25
|
+
screen: bool = False,
|
|
26
|
+
):
|
|
27
|
+
raw_audio_parameters = (
|
|
28
|
+
audio_parameters
|
|
29
|
+
if isinstance(audio_parameters, AudioParameters)
|
|
30
|
+
else AudioParameters(*audio_parameters.value)
|
|
31
|
+
if isinstance(audio_parameters, AudioQuality)
|
|
32
|
+
else ValueError('Invalid audio parameters')
|
|
33
|
+
)
|
|
34
|
+
super().__init__(
|
|
35
|
+
microphone=self._get_audio_stream(audio, raw_audio_parameters),
|
|
36
|
+
speaker=None,
|
|
37
|
+
camera=self._get_video_stream(camera),
|
|
38
|
+
screen=self._get_video_stream(screen),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _get_audio_stream(audio, raw_audio_parameters):
|
|
43
|
+
if isinstance(audio, bool) and audio:
|
|
44
|
+
return AudioStream(
|
|
45
|
+
media_source=MediaSource.EXTERNAL,
|
|
46
|
+
path='',
|
|
47
|
+
parameters=raw_audio_parameters,
|
|
48
|
+
)
|
|
49
|
+
if isinstance(audio, str):
|
|
50
|
+
is_lossless = raw_audio_parameters.bitrate > 48000
|
|
51
|
+
commands = [
|
|
52
|
+
'ffmpeg',
|
|
53
|
+
'-loglevel',
|
|
54
|
+
'quiet',
|
|
55
|
+
'-f',
|
|
56
|
+
's16le',
|
|
57
|
+
'-ar',
|
|
58
|
+
str(raw_audio_parameters.bitrate),
|
|
59
|
+
'-ac',
|
|
60
|
+
str(raw_audio_parameters.channels),
|
|
61
|
+
'-i',
|
|
62
|
+
'pipe:0',
|
|
63
|
+
'-codec:a',
|
|
64
|
+
'flac' if is_lossless else 'libmp3lame',
|
|
65
|
+
audio,
|
|
66
|
+
]
|
|
67
|
+
return AudioStream(
|
|
68
|
+
media_source=MediaSource.SHELL,
|
|
69
|
+
path=' '.join(commands),
|
|
70
|
+
parameters=raw_audio_parameters,
|
|
71
|
+
)
|
|
72
|
+
if isinstance(audio, SpeakerDevice):
|
|
73
|
+
return AudioStream(
|
|
74
|
+
media_source=MediaSource.DEVICE,
|
|
75
|
+
path=audio.metadata,
|
|
76
|
+
parameters=raw_audio_parameters,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _get_video_stream(enable):
|
|
81
|
+
return (
|
|
82
|
+
VideoStream(
|
|
83
|
+
media_source=MediaSource.EXTERNAL,
|
|
84
|
+
path='',
|
|
85
|
+
parameters=VideoParameters(
|
|
86
|
+
width=-1,
|
|
87
|
+
height=-1,
|
|
88
|
+
frame_rate=-1,
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
if enable
|
|
92
|
+
else None
|
|
93
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from enum import auto
|
|
2
|
+
|
|
3
|
+
from ntgcalls import StreamType
|
|
4
|
+
|
|
5
|
+
from ...types.update import Update
|
|
6
|
+
from ..flag import Flag
|
|
7
|
+
from .device import Device
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StreamEnded(Update):
|
|
11
|
+
|
|
12
|
+
class Type(Flag):
|
|
13
|
+
AUDIO = auto()
|
|
14
|
+
VIDEO = auto()
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def from_raw(kind: StreamType):
|
|
18
|
+
if kind == StreamType.AUDIO:
|
|
19
|
+
return StreamEnded.Type.AUDIO
|
|
20
|
+
if kind == StreamType.VIDEO:
|
|
21
|
+
return StreamEnded.Type.VIDEO
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
chat_id: int,
|
|
27
|
+
stream_type: Type,
|
|
28
|
+
device: Device,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(chat_id)
|
|
31
|
+
self.stream_type = stream_type
|
|
32
|
+
self.device = device
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from sys import platform
|
|
2
|
-
|
|
3
|
-
from ..types.py_object import PyObject
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ScreenInfo(PyObject):
|
|
7
|
-
def __init__(
|
|
8
|
-
self,
|
|
9
|
-
x: int,
|
|
10
|
-
y: int,
|
|
11
|
-
width: int,
|
|
12
|
-
height: int,
|
|
13
|
-
is_primary: bool,
|
|
14
|
-
title: str,
|
|
15
|
-
):
|
|
16
|
-
self.offset_x = x
|
|
17
|
-
self.offset_y = y
|
|
18
|
-
self.width = width
|
|
19
|
-
self.height = height
|
|
20
|
-
self.is_primary = is_primary
|
|
21
|
-
self.title = title
|
|
22
|
-
self.ffmpeg_parameters = ['-f']
|
|
23
|
-
|
|
24
|
-
def build_ffmpeg_command(self, frame_rate: int):
|
|
25
|
-
if platform == 'win32':
|
|
26
|
-
path = 'desktop'
|
|
27
|
-
|
|
28
|
-
self.ffmpeg_parameters += [
|
|
29
|
-
'gdigrab',
|
|
30
|
-
'-offset_x',
|
|
31
|
-
str(self.offset_x),
|
|
32
|
-
'-offset_y',
|
|
33
|
-
str(self.offset_y),
|
|
34
|
-
]
|
|
35
|
-
else:
|
|
36
|
-
path = f':0.0+{self.offset_x},{self.offset_y}'
|
|
37
|
-
self.ffmpeg_parameters += ['x11grab']
|
|
38
|
-
|
|
39
|
-
self.ffmpeg_parameters += [
|
|
40
|
-
'-video_size',
|
|
41
|
-
f'{self.width}x{self.height}',
|
|
42
|
-
'-framerate',
|
|
43
|
-
str(frame_rate),
|
|
44
|
-
]
|
|
45
|
-
return path
|
|
File without changes
|
|
File without changes
|