diff --git a/discord/voice_client.py b/discord/voice_client.py index c7d108e19..5b129e702 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -48,6 +48,8 @@ import struct import threading import subprocess import shlex +import functools +import datetime log = logging.getLogger(__name__) @@ -124,7 +126,6 @@ class ProcessPlayer(StreamPlayer): self.process.kill() super().stop() - class VoiceClient: """Represents a Discord voice connection. @@ -416,8 +417,11 @@ class VoiceClient: raise ClientException('Popen failed: {0.__name__} {1}'.format(type(e), str(e))) from e + @asyncio.coroutine def create_ytdl_player(self, url, *, ytdl_options=None, **kwargs): - """Creates a stream player for youtube or other services that launches + """|coro| + + Creates a stream player for youtube or other services that launches in a separate thread to play the audio. The player uses the ``youtube_dl`` python library to get the information @@ -429,7 +433,38 @@ class VoiceClient: variable in order for this to work. The operations that can be done on the player are the same as those in - :meth:`create_stream_player`. + :meth:`create_stream_player`. The player has been augmented and enhanced + to have some info extracted from the URL. If youtube-dl fails to extract + the information then the attribute is ``None``. The ``yt``, ``url``, and + ``download_url`` attributes are always available. + + +---------------------+---------------------------------------------------------+ + | Operation | Description | + +=====================+=========================================================+ + | player.yt | The `YoutubeDL ` instance. | + +---------------------+---------------------------------------------------------+ + | player.url | The URL that is currently playing. | + +---------------------+---------------------------------------------------------+ + | player.download_url | The URL that is currently being downloaded to ffmpeg. | + +---------------------+---------------------------------------------------------+ + | player.title | The title of the audio stream. | + +---------------------+---------------------------------------------------------+ + | player.description | The description of the audio stream. | + +---------------------+---------------------------------------------------------+ + | player.uploader | The uploader of the audio stream. | + +---------------------+---------------------------------------------------------+ + | player.upload_date | A datetime.date object of when the stream was uploaded. | + +---------------------+---------------------------------------------------------+ + | player.duration | The duration of the audio in seconds. | + +---------------------+---------------------------------------------------------+ + | player.likes | How many likes the audio stream has. | + +---------------------+---------------------------------------------------------+ + | player.dislikes | How many dislikes the audio stream has. | + +---------------------+---------------------------------------------------------+ + | player.is_live | Checks if the audio stream is currently livestreaming. | + +---------------------+---------------------------------------------------------+ + | player.views | How many views the audio stream has. | + +---------------------+---------------------------------------------------------+ .. _ytdl: https://github.com/rg3/youtube-dl/blob/master/youtube_dl/YoutubeDL.py#L117-L265 @@ -438,8 +473,8 @@ class VoiceClient: Basic usage: :: - voice = yield from client.join_voice_channel(channel) - player = voice.create_ytdl_player('https://www.youtube.com/watch?v=d62TYemN6MQ') + voice = await client.join_voice_channel(channel) + player = await voice.create_ytdl_player('https://www.youtube.com/watch?v=d62TYemN6MQ') player.start() Parameters @@ -462,14 +497,14 @@ class VoiceClient: Returns -------- StreamPlayer - A stream player with specific operations. - See :meth:`create_stream_player`. + An augmented StreamPlayer that uses ffmpeg. + See :meth:`create_stream_player` for base operations. """ import youtube_dl use_avconv = kwargs.get('use_avconv', False) opts = { - 'format': 'webm[abr>0]' if 'youtube' in url else 'best', + 'format': 'webm[abr>0]/bestaudio/best', 'prefer_ffmpeg': not use_avconv } @@ -477,9 +512,42 @@ class VoiceClient: opts.update(ytdl_options) ydl = youtube_dl.YoutubeDL(opts) - info = ydl.extract_info(url, download=False) + func = functools.partial(ydl.extract_info, url, download=False) + info = yield from self.loop.run_in_executor(None, func) log.info('playing URL {}'.format(url)) - return self.create_ffmpeg_player(info['url'], **kwargs) + download_url = info['url'] + player = self.create_ffmpeg_player(download_url, **kwargs) + + # set the dynamic attributes from the info extraction + player.download_url = download_url + player.url = url + player.yt = ydl + player.views = info.get('view_count') + player.is_live = bool(info.get('is_live')) + player.likes = info.get('like_count') + player.dislikes = info.get('dislike_count') + player.duration = info.get('duration') + player.uploader = info.get('uploader') + + is_twitch = 'twitch' in url + if is_twitch: + # twitch has 'title' and 'description' sort of mixed up. + player.title = info.get('description') + player.description = None + else: + player.title = info.get('title') + player.description = info.get('description') + + # upload date handling + date = info.get('upload_date') + if date: + try: + date = datetime.datetime.strptime(date, '%Y%M%d').date() + except ValueError: + date = None + + player.upload_date = date + return player def encoder_options(self, *, sample_rate, channels=2): """Sets the encoder options for the OpusEncoder.