Browse Source

Add VoiceClient.wait_until_done

Adds a new function: `VoiceClient.wait_until_done`.  As the name
suggests, it waits until the player is done, similar to the other
`wait_until_` functions.
pull/9842/head
Imayhaveborkedit 12 months ago
parent
commit
482044302f
  1. 16
      discord/player.py
  2. 19
      discord/voice_client.py

16
discord/player.py

@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
import threading import threading
@ -717,6 +718,8 @@ class AudioPlayer(threading.Thread):
self._current_error: Optional[Exception] = None self._current_error: Optional[Exception] = None
self._lock: threading.Lock = threading.Lock() self._lock: threading.Lock = threading.Lock()
self._end_async: asyncio.Event = asyncio.Event(loop=client.loop)
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.')
@ -774,7 +777,8 @@ class AudioPlayer(threading.Thread):
self.stop() self.stop()
finally: finally:
self._call_after() self._call_after()
self.source.cleanup() self._cleanup()
self.client.loop.call_soon_threadsafe(self._end_async.set)
def _call_after(self) -> None: def _call_after(self) -> None:
error = self._current_error error = self._current_error
@ -788,6 +792,12 @@ class AudioPlayer(threading.Thread):
elif error: elif error:
_log.exception('Exception in voice thread %s', self.name, exc_info=error) _log.exception('Exception in voice thread %s', self.name, exc_info=error)
def _cleanup(self) -> None:
try:
self.source.cleanup()
except Exception:
_log.exception("Error cleaning up audio source %s", self.source)
def stop(self) -> None: def stop(self) -> None:
self._end.set() self._end.set()
self._resumed.set() self._resumed.set()
@ -817,6 +827,10 @@ class AudioPlayer(threading.Thread):
self.source = source self.source = source
self.resume(update_speaking=False) self.resume(update_speaking=False)
async def wait_async(self) -> Optional[Exception]:
await self._end_async.wait()
return self._current_error
def _speak(self, speaking: SpeakingState) -> None: def _speak(self, speaking: SpeakingState) -> None:
try: try:
asyncio.run_coroutine_threadsafe(self.client.ws.speak(speaking), self.client.client.loop) asyncio.run_coroutine_threadsafe(self.client.ws.speak(speaking), self.client.client.loop)

19
discord/voice_client.py

@ -538,6 +538,25 @@ class VoiceClient(VoiceProtocol):
self._player.set_source(value) self._player.set_source(value)
async def wait_until_done(self) -> Optional[Exception]:
"""|coro|
Waits for the audio player to finish playback and returns any encountered error.
Returns
--------
Optional[:class:`Exception`]
The exception the player encountered during playback, or ``None``.
"""
if not self.is_connected():
raise ClientException('Not connected to voice.')
if self._player is None:
raise ValueError('Not playing anything.')
return await self._player.wait_async()
def send_audio_packet(self, data: bytes, *, encode: bool = True) -> None: def send_audio_packet(self, data: bytes, *, encode: bool = True) -> None:
"""Sends an audio packet composed of the data. """Sends an audio packet composed of the data.

Loading…
Cancel
Save