11"""
22The MIT License (MIT)
33
4- Copyright (c) 2015-2021 Rapptz & (c) 2021-present Pycord-Development
4+ Copyright (c) 2015-2021 Rapptz
5+ Copyright (c) 2021-present Pycord Development
56
67Permission is hereby granted, free of charge, to any person obtaining a
78copy of this software and associated documentation files (the "Software"),
2122FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2223DEALINGS IN THE SOFTWARE.
2324"""
24- import wave
25- import logging
2625import os
26+ import struct
27+ import sys
2728import threading
2829import time
29- import subprocess
30- import sys
31- import struct
32- from .errors import SinkException
3330
34- _log = logging . getLogger ( __name__ )
31+ from . errors import SinkException
3532
3633__all__ = (
3734 "Filters" ,
5754class Filters :
5855 """Filters for sink
5956
60- .. versionadded:: 2.0
57+ .. versionadded:: 2.1
6158
6259 Parameters
6360 ----------
64- interface: :meth:`Filters.interface`
65-
61+ container
62+ Container of all Filters.
6663 """
64+
6765 def __init__ (self , ** kwargs ):
6866 self .filtered_users = kwargs .get ("users" , default_filters ["users" ])
6967 self .seconds = kwargs .get ("time" , default_filters ["time" ])
7068 self .max_size = kwargs .get ("max_size" , default_filters ["max_size" ])
7169 self .finished = False
7270
7371 @staticmethod
74- def interface (func ): # Contains all filters
72+ def container (func ): # Contains all filters
7573 def _filter (self , data , user ):
7674 if not self .filtered_users or user in self .filtered_users :
7775 return func (self , data , user )
@@ -87,14 +85,13 @@ def wait_and_stop(self):
8785 time .sleep (self .seconds )
8886 if self .finished :
8987 return
90- self .vc .stop_listening ()
88+ self .vc .stop_recording ()
9189
9290
9391class RawData :
9492 """Handles raw data from Discord so that it can be decrypted and decoded to be used.
95-
96- .. versionadded:: 2.0
9793
94+ .. versionadded:: 2.1
9895 """
9996
10097 def __init__ (self , data , client ):
@@ -116,9 +113,7 @@ def __init__(self, data, client):
116113
117114class AudioData :
118115 """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
122117 Raises
123118 ------
124119 ClientException
@@ -158,40 +153,38 @@ def on_format(self, encoding):
158153class Sink (Filters ):
159154 """A Sink "stores" all the audio data.
160155
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
162170
163171 Parameters
164172 ----------
165- encoding: :class:`string`
166- The encoding to use. Valid types include wav, mp3, and pcm (even though it's not an actual encoding).
167173 output_path: :class:`string`
168174 A path to where the audio files should be output.
169-
175+
170176 Raises
171177 ------
172178 ClientException
173179 An invalid encoding type was specified.
174180 Audio may only be formatted after recording is finished.
175181 """
176182
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 ):
184184 if filters is None :
185185 filters = default_filters
186186 self .filters = filters
187187 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
195188 self .file_path = output_path
196189 self .vc = None
197190 self .audio_data = {}
@@ -200,7 +193,7 @@ def init(self, vc): # called under listen
200193 self .vc = vc
201194 super ().init ()
202195
203- @Filters .interface
196+ @Filters .container
204197 def write (self , data , user ):
205198 if user not in self .audio_data :
206199 ssrc = self .vc .get_ssrc (user )
@@ -214,55 +207,4 @@ def cleanup(self):
214207 self .finished = True
215208 for file in self .audio_data .values ():
216209 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