Browse Source

Fix various Python 3 voice issues

- Use print function for nacl library warning
- Use BufferedIO instead of StringIO on Python3, we're dealing with
bytes here
- Fix MRO issue w/ BufferedOpusEncoder
- Fix race condition on loading url information within YoutubeDLPlayable
feature/storage
Andrei 8 years ago
parent
commit
517c0037ee
  1. 4
      disco/voice/client.py
  2. 54
      disco/voice/playable.py
  3. 4
      examples/music.py
  4. 1
      requirements-optional.txt

4
disco/voice/client.py

@ -1,3 +1,5 @@
from __future__ import print_function
import gevent
import socket
import struct
@ -6,7 +8,7 @@ import time
try:
import nacl.secret
except ImportError:
print 'WARNING: nacl is not installed, voice support is disabled'
print('WARNING: nacl is not installed, voice support is disabled')
from holster.enum import Enum
from holster.emitter import Emitter

54
disco/voice/playable.py

@ -5,28 +5,24 @@ import gevent
import struct
import subprocess
from gevent.lock import Semaphore
from gevent.queue import Queue
from disco.voice.opus import OpusEncoder
try:
from cStringIO import cStringIO as StringIO
from cStringIO import cStringIO as BufferedIO
except:
from six import StringIO
if six.PY2:
from StringIO import StringIO as BufferedIO
else:
from io import BytesIO as BufferedIO
OPUS_HEADER_SIZE = struct.calcsize('<h')
# Play from file:
# OpusFilePlayable(open('myfile.opus', 'r'))
# PCMFileInput(open('myfile.pcm', 'r')).pipe(DCADOpusEncoder) => OpusPlayable
# FFMpegInput.youtube_dl('youtube.com/yolo').pipe(DCADOpusEncoder) => OpusPlayable
# FFMpegInput.youtube_dl('youtube.com/yolo').pipe(OpusEncoder).pipe(DuplexStream, open('cache_file.opus', 'w')) => OpusPlayable
class AbstractOpus(object):
def __init__(self, sampling_rate=48000, frame_length=20, channels=2):
self.sampling_rate = sampling_rate
@ -116,7 +112,7 @@ class FFmpegInput(BaseInput, AbstractOpus):
# First read blocks until the subprocess finishes
if not self._buffer:
data, _ = self.proc.communicate()
self._buffer = StringIO(data)
self._buffer = BufferedIO(data)
# Subsequent reads can just do dis thang
return self._buffer.read(sz)
@ -155,22 +151,24 @@ class YoutubeDLInput(FFmpegInput):
self._url = url
self._ie_info = ie_info
self._info = None
self._info_lock = Semaphore()
@property
def info(self):
if not self._info:
import youtube_dl
ydl = youtube_dl.YoutubeDL({'format': 'webm[abr>0]/bestaudio/best'})
with self._info_lock:
if not self._info:
import youtube_dl
ydl = youtube_dl.YoutubeDL({'format': 'webm[abr>0]/bestaudio/best'})
if self._url:
obj = ydl.extract_info(self._url, download=False, process=False)
if 'entries' in obj:
self._ie_info = obj['entries']
else:
self._ie_info = [obj]
if self._url:
obj = ydl.extract_info(self._url, download=False, process=False)
if 'entries' in obj:
self._ie_info = obj['entries'][0]
else:
self._ie_info = obj
self._info = ydl.process_ie_result(self._ie_info, download=False)
return self._info
self._info = ydl.process_ie_result(self._ie_info, download=False)
return self._info
@property
def _metadata(self):
@ -195,11 +193,19 @@ class YoutubeDLInput(FFmpegInput):
return self.info['url']
class BufferedOpusEncoderPlayable(BasePlayable, AbstractOpus, OpusEncoder):
class BufferedOpusEncoderPlayable(BasePlayable, OpusEncoder, AbstractOpus):
def __init__(self, source, *args, **kwargs):
self.source = source
self.frames = Queue(kwargs.pop('queue_size', 4096))
super(BufferedOpusEncoderPlayable, self).__init__(*args, **kwargs)
# Call the AbstractOpus constructor, as we need properties it sets
AbstractOpus.__init__(self, *args, **kwargs)
# Then call the OpusEncoder constructor, which requires some properties
# that AbstractOpus sets up
OpusEncoder.__init__(self, self.sampling_rate, self.channels)
# Spawn the encoder loop
gevent.spawn(self._encoder_loop)
def _encoder_loop(self):

4
examples/music.py

@ -1,7 +1,7 @@
from disco.bot import Plugin
from disco.bot.command import CommandError
from disco.voice.player import Player
from disco.voice.playable import FFmpegInput, DCADOpusEncoderPlayable
from disco.voice.playable import YoutubeDLInput, BufferedOpusEncoderPlayable
from disco.voice.client import VoiceException
@ -40,7 +40,7 @@ class MusicPlugin(Plugin):
@Plugin.command('play', '<url:str>')
def on_play(self, event, url):
item = FFmpegInput.youtube_dl(url).pipe(DCADOpusEncoderPlayable)
item = YoutubeDLInput(url).pipe(BufferedOpusEncoderPlayable)
self.get_player(event.guild.id).queue.put(item)
@Plugin.command('pause')

1
requirements-optional.txt

@ -1 +1,2 @@
pynacl==1.1.2
youtube-dl==2017.4.17

Loading…
Cancel
Save