예제 #1
0
    def __init__(self, track, num, r_range):
        """
        :type track: plugin.track.Track
        :type num: int
        :type r_range: plugin.range.Range
        """
        self.track = track
        self.server = track.server

        self.stream_num = num
        self.range = r_range

        # HTTP request/response
        self.request = None
        self.response = None
        self.headers = None

        # Content info
        self.content_range = None
        self.content_length = None
        self.total_length = None

        # Data buffering
        self.read_thread = None
        self.read_sleep = None

        self.buffer = bytearray()

        self.on_open = REvent()
        self.on_reading = Event()

        self.state = ''

        self.request_lock = Lock()
        self.request_seq = 0
예제 #2
0
    def __init__(self, track, num, r_range):
        """
        :type track: plugin.track.Track
        :type num: int
        :type r_range: plugin.range.Range
        """
        self.track = track
        self.server = track.server

        self.stream_num = num
        self.range = r_range

        # HTTP request/response
        self.request = None
        self.response = None
        self.headers = None

        # Content info
        self.content_range = None
        self.content_length = None
        self.total_length = None

        # Data buffering
        self.read_thread = None
        self.read_sleep = None

        self.buffer = bytearray()

        self.on_open = REvent()
        self.on_reading = Event()

        self.state = ''

        self.request_lock = Lock()
        self.request_seq = 0
예제 #3
0

def plugin_callback(method, kwargs=None, async=False):
    """ Invokes callbacks on the plugin instance

    :param method:     The method on the SpotifyPlugin class to call.
    :param kwargs:     A dictionary of keyward args to pass to the method.
    """

    Log.Debug('plugin_callback - method: %s, kwargs: %s, async: %s' % (method, kwargs, async))

    kwargs = kwargs or {}
    result = None

    if async:
        on_complete = REvent()

        kwargs['callback'] = lambda result: on_complete.set(result)

        method(host, **kwargs)

        result = on_complete.wait(30)
    else:
        result = method(host, **kwargs)

    if result is None:
        if async:
            return MessageContainer(
                header=L("MSG_CALLBACK_TIMEOUT_TITLE"),
                message=L("MSG_CALLBACK_TIMEOUT_BODY")
            )
예제 #4
0
class Stream(Emitter):
    chunk_size = 10240  # 10kB

    def __init__(self, track, num, r_range):
        """
        :type track: plugin.track.Track
        :type num: int
        :type r_range: plugin.range.Range
        """
        self.track = track
        self.server = track.server

        self.stream_num = num
        self.range = r_range

        # HTTP request/response
        self.request = None
        self.response = None
        self.headers = None

        # Content info
        self.content_range = None
        self.content_length = None
        self.total_length = None

        # Data buffering
        self.read_thread = None
        self.read_sleep = None

        self.buffer = bytearray()

        self.on_open = REvent()
        self.on_reading = Event()

        self.state = ''

        self.request_lock = Lock()
        self.request_seq = 0

    def log(self, message, *args, **kwargs):
        header = '[%s] [%s] ' % (self.track.uri, self.stream_num)
        log.info(header + str(message), *args, **kwargs)

    def prepare(self):
        headers = {}

        if self.range:
            headers['Range'] = str(self.range)

        return headers

    def open(self):
        if self.state != '':
            return

        self.state = 'opening'
        self.emit('opening')

        future = self.server.session.get(self.track.info['uri'],
                                         headers=self.prepare(),
                                         stream=True)
        future.add_done_callback(self.callback)

    def callback(self, future):
        ex = future.exception()

        if ex:
            log.warn('Request failed: %s', ex)
            self.on_open.set(False)
            return

        self.response = future.result()
        self.headers = self.response.headers

        self.state = 'opened'
        self.emit('opened')

        self.content_length = int(self.headers.get('Content-Length'))
        self.log('Content-Length: %s', self.content_length)

        self.content_range = ContentRange.parse(
            self.headers.get('Content-Range'))
        self.log('Content-Range: %s', self.content_range)

        if self.content_range:
            self.total_length = self.content_range.length
        else:
            # Build dummy ContentRange
            self.content_range = ContentRange(start=0,
                                              end=self.content_length - 1,
                                              length=self.content_length)

            self.total_length = self.content_length

        self.log('Total-Length: %s', self.total_length)

        if self.headers.get('Content-Type') == 'text/xml':
            # Error, log response
            self.log(self.response.content)
            self.on_open.set(False)
            return

        # Read back entire stream
        self.on_reading.set()

        self.read_thread = Thread(target=func_catch, args=(self.run, ))
        self.read_thread.start()

    def run(self):
        self.state = 'reading'
        self.emit('reading')
        self.on_open.set(True)

        last_progress = None

        for chunk in self.response.iter_content(self.chunk_size):
            self.buffer.extend(chunk)
            self.emit('received', len(chunk), __suppress=True)

            log.log(logging.TRACE, '[%s] [%s] Received chunk - len(chunk): %s',
                    self.track.uri, self.stream_num, len(chunk))

            last_progress = log_progress(self,
                                         '[%s]     Reading' % self.stream_num,
                                         len(self.buffer), last_progress)

            self.on_reading.wait()

        self.state = 'buffered'
        self.emit('buffered')

    def iter(self, c_range):
        """
        :type c_range: plugin.range.ContentRange
        """
        with self.request_lock:
            num = self.request_seq
            self.request_seq += 1

        position = 0
        end = self.content_range.end + 1

        last_progress = None
        ev_received = Event()

        @self.on(['received', 'buffered'])
        def on_received(*args):
            ev_received.set()

        if c_range:
            position = c_range.start - self.content_range.start
            end = (c_range.end - self.content_range.start) + 1

            log.info(
                '[%s] [%s:%s] Streaming - c_range: %s, position: %s, end: %s',
                self.track.uri, self.stream_num, num, c_range, position, end)

        while position < self.content_length:
            chunk_size = end - position

            # Clamp to maximum `chunk_size`
            if chunk_size > self.chunk_size:
                chunk_size = self.chunk_size

            # Check if range has reached the end
            if not chunk_size:
                log.debug('[%s] [%s:%s] Range complete', self.track.uri,
                          self.stream_num, num)
                break

            chunk = self.buffer[position:position + chunk_size]

            if chunk:
                log.log(logging.TRACE,
                        '[%s] [%s:%s] Sending chunk - len(buffer[%s:%s]): %s',
                        self.track.uri, self.stream_num, num, position,
                        position + chunk_size, len(chunk))

                last_progress = log_progress(self,
                                             '[%s:%s] Streaming' %
                                             (self.stream_num, num),
                                             position,
                                             last_progress,
                                             length=end)
                position += len(chunk)
                yield str(chunk)
            elif self.state != 'buffered':
                log.log(logging.TRACE, '[%s] [%s:%s] Waiting for buffer',
                        self.track.uri, self.stream_num, num)

                ev_received.clear()
                ev_received.wait()
            else:
                log.debug('[%s] [%s:%s] Buffer doesn\'t contain range [%s:%s]',
                          self.track.uri, self.stream_num, num, position,
                          position + chunk_size)
                break

        self.off('received', on_received)\
            .off('buffered', on_received)

        log.info('[%s] [%s:%s] Complete', self.track.uri, self.stream_num, num)
예제 #5
0
class Stream(Emitter):
    chunk_size = 10240  # 10kB

    def __init__(self, track, num, r_range):
        """
        :type track: plugin.track.Track
        :type num: int
        :type r_range: plugin.range.Range
        """
        self.track = track
        self.server = track.server

        self.stream_num = num
        self.range = r_range

        # HTTP request/response
        self.request = None
        self.response = None
        self.headers = None

        # Content info
        self.content_range = None
        self.content_length = None
        self.total_length = None

        # Data buffering
        self.read_thread = None
        self.read_sleep = None

        self.buffer = bytearray()

        self.on_open = REvent()
        self.on_reading = Event()

        self.state = ''

        self.request_lock = Lock()
        self.request_seq = 0

    def log(self, message, *args, **kwargs):
        header = '[%s] [%s] ' % (self.track.uri, self.stream_num)
        log.info(header + str(message), *args, **kwargs)

    def prepare(self):
        headers = {}

        if self.range:
            headers['Range'] = str(self.range)

        return headers

    def open(self):
        if self.state != '':
            return

        self.state = 'opening'
        self.emit('opening')

        future = self.server.session.get(
            self.track.info['uri'],
            headers=self.prepare(),
            stream=True
        )
        future.add_done_callback(self.callback)

    def callback(self, future):
        ex = future.exception()

        if ex:
            log.warn('Request failed: %s', ex)
            self.on_open.set(False)
            return

        self.response = future.result()
        self.headers = self.response.headers

        self.state = 'opened'
        self.emit('opened')

        self.content_length = int(self.headers.get('Content-Length'))
        self.log('Content-Length: %s', self.content_length)

        self.content_range = ContentRange.parse(self.headers.get('Content-Range'))
        self.log('Content-Range: %s', self.content_range)

        if self.content_range:
            self.total_length = self.content_range.length
        else:
            # Build dummy ContentRange
            self.content_range = ContentRange(
                start=0,
                end=self.content_length - 1,
                length=self.content_length
            )

            self.total_length = self.content_length

        self.log('Total-Length: %s', self.total_length)

        if self.headers.get('Content-Type') == 'text/xml':
            # Error, log response
            self.log(self.response.content)
            self.on_open.set(False)
            return

        # Read back entire stream
        self.on_reading.set()

        self.read_thread = Thread(target=func_catch, args=(self.run,))
        self.read_thread.start()

    def run(self):
        self.state = 'reading'
        self.emit('reading')
        self.on_open.set(True)

        last_progress = None

        for chunk in self.response.iter_content(self.chunk_size):
            self.buffer.extend(chunk)
            self.emit('received', len(chunk), __suppress=True)

            log.log(
                logging.TRACE,
                '[%s] [%s] Received chunk - len(chunk): %s',
                self.track.uri, self.stream_num, len(chunk)
            )

            last_progress = log_progress(self, '[%s]     Reading' % self.stream_num, len(self.buffer), last_progress)

            self.on_reading.wait()

        self.state = 'buffered'
        self.emit('buffered')

    def iter(self, c_range):
        """
        :type c_range: plugin.range.ContentRange
        """
        with self.request_lock:
            num = self.request_seq
            self.request_seq += 1

        position = 0
        end = self.content_range.end + 1

        last_progress = None
        ev_received = Event()

        @self.on(['received', 'buffered'])
        def on_received(*args):
            ev_received.set()

        if c_range:
            position = c_range.start - self.content_range.start
            end = (c_range.end - self.content_range.start) + 1

            log.info(
                '[%s] [%s:%s] Streaming - c_range: %s, position: %s, end: %s',
                self.track.uri, self.stream_num,
                num, c_range, position, end
            )

        while position < self.content_length:
            chunk_size = end - position

            # Clamp to maximum `chunk_size`
            if chunk_size > self.chunk_size:
                chunk_size = self.chunk_size

            # Check if range has reached the end
            if not chunk_size:
                log.debug(
                    '[%s] [%s:%s] Range complete',
                    self.track.uri, self.stream_num, num
                )
                break

            chunk = self.buffer[position:position + chunk_size]

            if chunk:
                log.log(
                    logging.TRACE,
                    '[%s] [%s:%s] Sending chunk - len(buffer[%s:%s]): %s',
                    self.track.uri, self.stream_num, num,
                    position, position + chunk_size,
                    len(chunk)
                )

                last_progress = log_progress(
                    self, '[%s:%s] Streaming' % (self.stream_num, num),
                    position, last_progress, length=end
                )
                position += len(chunk)
                yield str(chunk)
            elif self.state != 'buffered':
                log.log(
                    logging.TRACE,
                    '[%s] [%s:%s] Waiting for buffer',
                    self.track.uri, self.stream_num, num
                )

                ev_received.clear()
                ev_received.wait()
            else:
                log.debug(
                    '[%s] [%s:%s] Buffer doesn\'t contain range [%s:%s]',
                    self.track.uri, self.stream_num, num,
                    position, position + chunk_size
                )
                break

        self.off('received', on_received)\
            .off('buffered', on_received)

        log.info(
            '[%s] [%s:%s] Complete',
            self.track.uri, self.stream_num,
            num
        )