mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-13 17:29:50 +00:00
Catch Player errors and gracefully stop them.
This also introduces the concept of the after function taking a single parameter, the current player. This is useful for error handling, e.g. checking Player.error. Fixes #291
This commit is contained in:
parent
96ca7cafee
commit
6fec17d7d4
@ -51,6 +51,7 @@ import shlex
|
|||||||
import functools
|
import functools
|
||||||
import datetime
|
import datetime
|
||||||
import audioop
|
import audioop
|
||||||
|
import inspect
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -78,11 +79,12 @@ class StreamPlayer(threading.Thread):
|
|||||||
self.after = after
|
self.after = after
|
||||||
self.delay = encoder.frame_length / 1000.0
|
self.delay = encoder.frame_length / 1000.0
|
||||||
self._volume = 1.0
|
self._volume = 1.0
|
||||||
|
self._current_error = None
|
||||||
|
|
||||||
if after is not None and not callable(after):
|
if after is not None and not callable(after):
|
||||||
raise TypeError('Expected a callable for the "after" parameter.')
|
raise TypeError('Expected a callable for the "after" parameter.')
|
||||||
|
|
||||||
def run(self):
|
def _do_run(self):
|
||||||
self.loops = 0
|
self.loops = 0
|
||||||
self._start = time.time()
|
self._start = time.time()
|
||||||
while not self._end.is_set():
|
while not self._end.is_set():
|
||||||
@ -110,14 +112,34 @@ class StreamPlayer(threading.Thread):
|
|||||||
delay = max(0, self.delay + (next_time - time.time()))
|
delay = max(0, self.delay + (next_time - time.time()))
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
self._do_run()
|
||||||
|
except Exception as e:
|
||||||
|
self._current_error = e
|
||||||
|
self.stop()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._end.set()
|
self._end.set()
|
||||||
if self.after is not None:
|
if self.after is not None:
|
||||||
try:
|
try:
|
||||||
|
arg_count = len(inspect.signature(self.after).parameters)
|
||||||
|
except:
|
||||||
|
# if this ended up happening, a mistake was made.
|
||||||
|
arg_count = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
if arg_count == 0:
|
||||||
self.after()
|
self.after()
|
||||||
|
else:
|
||||||
|
self.after(self)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error(self):
|
||||||
|
return self._current_error
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume(self):
|
def volume(self):
|
||||||
return self._volume
|
return self._volume
|
||||||
@ -580,7 +602,8 @@ class VoiceClient:
|
|||||||
The stream player assumes that ``stream.read`` is a valid function
|
The stream player assumes that ``stream.read`` is a valid function
|
||||||
that returns a *bytes-like* object.
|
that returns a *bytes-like* object.
|
||||||
|
|
||||||
The finalizer, ``after`` is called after the stream has been exhausted.
|
The finalizer, ``after`` is called after the stream has been exhausted
|
||||||
|
or an error occurred (see below).
|
||||||
|
|
||||||
The following operations are valid on the ``StreamPlayer`` object:
|
The following operations are valid on the ``StreamPlayer`` object:
|
||||||
|
|
||||||
@ -603,20 +626,29 @@ class VoiceClient:
|
|||||||
| | equivalent to 100% and 0.0 is equal to 0%. The |
|
| | equivalent to 100% and 0.0 is equal to 0%. The |
|
||||||
| | maximum the volume can be set to is 2.0 for 200%. |
|
| | maximum the volume can be set to is 2.0 for 200%. |
|
||||||
+---------------------+-----------------------------------------------------+
|
+---------------------+-----------------------------------------------------+
|
||||||
|
| player.error | The exception that stopped the player. If no error |
|
||||||
|
| | happened, then this returns None. |
|
||||||
|
+---------------------+-----------------------------------------------------+
|
||||||
|
|
||||||
The stream must have the same sampling rate as the encoder and the same
|
The stream must have the same sampling rate as the encoder and the same
|
||||||
number of channels. The defaults are 48000 Hz and 2 channels. You
|
number of channels. The defaults are 48000 Hz and 2 channels. You
|
||||||
could change the encoder options by using :meth:`encoder_options`
|
could change the encoder options by using :meth:`encoder_options`
|
||||||
but this must be called **before** this function.
|
but this must be called **before** this function.
|
||||||
|
|
||||||
|
If an error happens while the player is running, the exception is caught and
|
||||||
|
the player is then stopped. The caught exception could then be retrieved
|
||||||
|
via ``player.error``\. When the player is stopped in this matter, the
|
||||||
|
finalizer under ``after`` is called.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
-----------
|
||||||
stream
|
stream
|
||||||
The stream object to read from.
|
The stream object to read from.
|
||||||
after
|
after
|
||||||
The finalizer that is called after the stream is exhausted.
|
The finalizer that is called after the stream is exhausted.
|
||||||
All exceptions it throws are silently discarded. It is called
|
All exceptions it throws are silently discarded. This function
|
||||||
without parameters.
|
can have either no parameters or a single parameter taking in the
|
||||||
|
current player.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
--------
|
--------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user