1
1
"""
2
2
The MIT License (MIT)
3
3
4
- Copyright (c) 2015-2021 Rapptz & (c) 2021-present Pycord-Development
4
+ Copyright (c) 2015-2021 Rapptz
5
+ Copyright (c) 2021-present Pycord Development
5
6
6
7
Permission is hereby granted, free of charge, to any person obtaining a
7
8
copy of this software and associated documentation files (the "Software"),
21
22
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
23
DEALINGS IN THE SOFTWARE.
23
24
"""
24
- import wave
25
- import logging
26
25
import os
26
+ import struct
27
+ import sys
27
28
import threading
28
29
import time
29
- import subprocess
30
- import sys
31
- import struct
32
- from .errors import SinkException
33
30
34
- _log = logging . getLogger ( __name__ )
31
+ from . errors import SinkException
35
32
36
33
__all__ = (
37
34
"Filters" ,
57
54
class Filters :
58
55
"""Filters for sink
59
56
60
- .. versionadded:: 2.0
57
+ .. versionadded:: 2.1
61
58
62
59
Parameters
63
60
----------
64
- interface: :meth:`Filters.interface`
65
-
61
+ container
62
+ Container of all Filters.
66
63
"""
64
+
67
65
def __init__ (self , ** kwargs ):
68
66
self .filtered_users = kwargs .get ("users" , default_filters ["users" ])
69
67
self .seconds = kwargs .get ("time" , default_filters ["time" ])
70
68
self .max_size = kwargs .get ("max_size" , default_filters ["max_size" ])
71
69
self .finished = False
72
70
73
71
@staticmethod
74
- def interface (func ): # Contains all filters
72
+ def container (func ): # Contains all filters
75
73
def _filter (self , data , user ):
76
74
if not self .filtered_users or user in self .filtered_users :
77
75
return func (self , data , user )
@@ -87,14 +85,13 @@ def wait_and_stop(self):
87
85
time .sleep (self .seconds )
88
86
if self .finished :
89
87
return
90
- self .vc .stop_listening ()
88
+ self .vc .stop_recording ()
91
89
92
90
93
91
class RawData :
94
92
"""Handles raw data from Discord so that it can be decrypted and decoded to be used.
95
-
96
- .. versionadded:: 2.0
97
93
94
+ .. versionadded:: 2.1
98
95
"""
99
96
100
97
def __init__ (self , data , client ):
@@ -116,9 +113,7 @@ def __init__(self, data, client):
116
113
117
114
class AudioData :
118
115
"""Handles data that's been completely decrypted and decoded and is ready to be saved to file.
119
-
120
- .. versionadded:: 2.0
121
-
116
+ .. versionadded:: 2.1
122
117
Raises
123
118
------
124
119
ClientException
@@ -158,40 +153,38 @@ def on_format(self, encoding):
158
153
class Sink (Filters ):
159
154
"""A Sink "stores" all the audio data.
160
155
161
- .. versionadded:: 2.0
156
+ Can be subclassed for extra customizablilty,
157
+
158
+ .. warning::
159
+ It is although recommended you use,
160
+ the officially provided sink classes
161
+ like :class:`~discord.sinks.WaveSink`
162
+
163
+ just replace the following like so: ::
164
+ vc.start_recording(
165
+ MySubClassedSink(),
166
+ finished_callback,
167
+ ctx.channel,
168
+ )
169
+ .. versionadded:: 2.1
162
170
163
171
Parameters
164
172
----------
165
- encoding: :class:`string`
166
- The encoding to use. Valid types include wav, mp3, and pcm (even though it's not an actual encoding).
167
173
output_path: :class:`string`
168
174
A path to where the audio files should be output.
169
-
175
+
170
176
Raises
171
177
------
172
178
ClientException
173
179
An invalid encoding type was specified.
174
180
Audio may only be formatted after recording is finished.
175
181
"""
176
182
177
- valid_encodings = [
178
- "wav" ,
179
- "mp3" ,
180
- "pcm" ,
181
- ]
182
-
183
- def __init__ (self , * , encoding = "wav" , output_path = "" , filters = None ):
183
+ def __init__ (self , * , output_path = "" , filters = None ):
184
184
if filters is None :
185
185
filters = default_filters
186
186
self .filters = filters
187
187
Filters .__init__ (self , ** self .filters )
188
-
189
- encoding = encoding .lower ()
190
-
191
- if encoding not in self .valid_encodings :
192
- raise SinkException ("An invalid encoding type was specified." )
193
-
194
- self .encoding = encoding
195
188
self .file_path = output_path
196
189
self .vc = None
197
190
self .audio_data = {}
@@ -200,7 +193,7 @@ def init(self, vc): # called under listen
200
193
self .vc = vc
201
194
super ().init ()
202
195
203
- @Filters .interface
196
+ @Filters .container
204
197
def write (self , data , user ):
205
198
if user not in self .audio_data :
206
199
ssrc = self .vc .get_ssrc (user )
@@ -214,55 +207,4 @@ def cleanup(self):
214
207
self .finished = True
215
208
for file in self .audio_data .values ():
216
209
file .cleanup ()
217
- self .format_audio (file )
218
-
219
- def format_audio (self , audio ):
220
- if self .vc .recording :
221
- raise SinkException (
222
- "Audio may only be formatted after recording is finished."
223
- )
224
- if self .encoding == "pcm" :
225
- return
226
- if self .encoding == "mp3" :
227
- mp3_file = audio .file .split ("." )[0 ] + ".mp3"
228
- args = [
229
- "ffmpeg" ,
230
- "-f" ,
231
- "s16le" ,
232
- "-ar" ,
233
- "48000" ,
234
- "-ac" ,
235
- "2" ,
236
- "-i" ,
237
- audio .file ,
238
- mp3_file ,
239
- ]
240
- process = None
241
- if os .path .exists (mp3_file ):
242
- os .remove (
243
- mp3_file
244
- ) # process will get stuck asking whether or not to overwrite, if file already exists.
245
- try :
246
- process = subprocess .Popen (args , creationflags = CREATE_NO_WINDOW )
247
- except FileNotFoundError :
248
- raise SinkException ("ffmpeg was not found." ) from None
249
- except subprocess .SubprocessError as exc :
250
- raise SinkException (
251
- "Popen failed: {0.__class__.__name__}: {0}" .format (exc )
252
- ) from exc
253
- process .wait ()
254
- elif self .encoding == "wav" :
255
- with open (audio .file , "rb" ) as pcm :
256
- data = pcm .read ()
257
- pcm .close ()
258
-
259
- wav_file = audio .file .split ("." )[0 ] + ".wav"
260
- with wave .open (wav_file , "wb" ) as f :
261
- f .setnchannels (self .vc .decoder .CHANNELS )
262
- f .setsampwidth (self .vc .decoder .SAMPLE_SIZE // self .vc .decoder .CHANNELS )
263
- f .setframerate (self .vc .decoder .SAMPLING_RATE )
264
- f .writeframes (data )
265
- f .close ()
266
-
267
- os .remove (audio .file )
268
- audio .on_format (self .encoding )
210
+ self .format_audio (file )
0 commit comments