import abc import six import types 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 BufferedIO except: if six.PY2: from StringIO import StringIO as BufferedIO else: from io import BytesIO as BufferedIO OPUS_HEADER_SIZE = struct.calcsize('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'][0] else: self._ie_info = obj self._info = ydl.process_ie_result(self._ie_info, download=False) return self._info @property def _metadata(self): return self.info @classmethod def many(cls, url, *args, **kwargs): import youtube_dl ydl = youtube_dl.YoutubeDL({'format': 'webm[abr>0]/bestaudio/best'}) info = ydl.extract_info(url, download=False, process=False) if 'entries' not in info: yield cls(ie_info=info, *args, **kwargs) return for item in info['entries']: yield cls(ie_info=item, *args, **kwargs) @property def source(self): return self.info['url'] class BufferedOpusEncoderPlayable(BasePlayable, OpusEncoder, AbstractOpus): def __init__(self, source, *args, **kwargs): self.source = source self.frames = Queue(kwargs.pop('queue_size', 4096)) # 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): while self.source: raw = self.source.read(self.frame_size) if len(raw) < self.frame_size: break self.frames.put(self.encode(raw, self.samples_per_frame)) gevent.idle() self.source = None self.frames.put(None) def next_frame(self): return self.frames.get() class DCADOpusEncoderPlayable(BasePlayable, AbstractOpus, OpusEncoder): def __init__(self, source, *args, **kwargs): self.source = source self.command = kwargs.pop('command', 'dcad') self.on_complete = kwargs.pop('on_complete', None) super(DCADOpusEncoderPlayable, self).__init__(*args, **kwargs) self._done = False self._proc = None @property def proc(self): if not self._proc: source = obj = self.source.fileobj() if not hasattr(obj, 'fileno'): source = subprocess.PIPE self._proc = subprocess.Popen([ self.command, '--channels', str(self.channels), '--rate', str(self.sampling_rate), '--size', str(self.samples_per_frame), '--bitrate', '128', '--fec', '--packet-loss-percent', '30', '--input', 'pipe:0', '--output', 'pipe:1', ], stdin=source, stdout=subprocess.PIPE) def writer(): while True: data = obj.read(2048) if len(data) > 0: self._proc.stdin.write(data) if len(data) < 2048: break if source == subprocess.PIPE: gevent.spawn(writer) return self._proc def next_frame(self): if self._done: return None header = self.proc.stdout.read(OPUS_HEADER_SIZE) if len(header) < OPUS_HEADER_SIZE: self._done = True self.on_complete() return size = struct.unpack('