def handle(self, connection):
        # 30 minutes cache
        if not self.playlist or (gevent.time.time() - self.playlisttime > 30 * 60):
           if not self.Playlistparser(): connection.send_error()

        connection.ext = query_get(connection.query, 'ext', 'ts')
        if connection.path.startswith('/{reqtype}/channel/'.format(**connection.__dict__)):
           if not connection.path.endswith(connection.ext):
              connection.send_error(404, 'Invalid path: {path}'.format(**connection.__dict__), logging.ERROR)
           name = ensure_text(unquote(os.path.splitext(os.path.basename(connection.path))[0]))
           url = self.channels.get(name)
           if url is None:
              connection.send_error(404, '[%s]: unknown channel: %s' % (self.__class__.__name__, name), logging.ERROR)
           connection.__dict__.update({'channelName': name,
                                       'channelIcon': self.picons.get(name),
                                       'path': {'acestream': '/content_id/%s/%s.%s' % (urlparse(url).netloc, name, connection.ext),
                                                'infohash' : '/infohash/%s/%s.%s' % (urlparse(url).netloc, name, connection.ext),
                                                'http'     : '/url/%s/%s.%s' % (quote(url,''), name, connection.ext),
                                                'https'    : '/url/%s/%s.%s' % (quote(url,''), name, connection.ext),
                                               }[urlparse(url).scheme]})
           connection.__dict__.update({'splittedpath': connection.path.split('/')})
           connection.__dict__.update({'reqtype': connection.splittedpath[1].lower()})
           return

        elif self.etag == connection.headers.get('If-None-Match'):
           logging.debug('[%s]: ETag matches. Return 304 to [%s]' % (self.__class__.__name__, connection.clientip))
           connection.send_response(304)
           connection.send_header('Connection', 'close')
           connection.end_headers()
           return

        else:
           exported = self.playlist.exportm3u( hostport=connection.headers['Host'],
                                               path='' if not self.channels else '/{reqtype}/channel'.format(**connection.__dict__),
                                               header=config.m3uheadertemplate,
                                               query=connection.query
                                              )
           response_headers = {'Content-Type': 'audio/mpegurl; charset=utf-8', 'Connection': 'close', 'Access-Control-Allow-Origin': '*'}
           try:
              h = connection.headers.get('Accept-Encoding').split(',')[0]
              compress_method = { 'zlib': zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS),
                                  'deflate': zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS),
                                  'gzip': zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16) }
              exported = compress_method[h].compress(exported) + compress_method[h].flush()
              response_headers['Content-Encoding'] = h
           except: pass
           response_headers['Content-Length'] = len(exported)
           if connection.request_version == 'HTTP/1.1':
              response_headers['ETag'] = self.etag
           connection.send_response(200)
           gevent.joinall([gevent.spawn(connection.send_header, k, v) for (k,v) in response_headers.items()])
           connection.end_headers()
           connection.wfile.write(exported)
           logging.debug('[%s]: plugin sent playlist to [%s]' % (self.__class__.__name__, connection.clientip))
Пример #2
0
def StreamReader(params):
    '''
    params: dict([url=] [file_index=] [infohash= ] [ad=1 [interruptable=1]] [stream=1] [pos=position] [bitrate=] [length=])
    '''
    def checkBroadcast():
        if not params['broadcastclients']:
            params['broadcast'].kill()
        return params['broadcast'].started

    def write_chunk(client, chunk, timeout=15.0):
        try:
            client.q.put(chunk, timeout=timeout)
        except gevent.queue.Full:
            client.send_error(
                500, '[%s]: does not read data until %s sec' %
                (client.clientip, timeout), logging.ERROR)

    def StreamWriter(url):
        for chunk in s.get(url,
                           timeout=(5, AceConfig.videotimeout),
                           stream=True).iter_content(chunk_size=1048576):
            _ = params['chunkpool'].map(
                lambda client: write_chunk(client, chunk),
                params['broadcastclients'])

    try:
        params.update({
            'url':
            urlparse(unquote(params['url']))._replace(
                netloc='{aceHostIP}:{aceHTTPport}'.format(
                    **AceConfig.ace)).geturl(),
            'broadcast':
            gevent.getcurrent(),
            'broadcastclients':
            AceProxy.clientcounter.getClientsList(params['infohash']),
            'chunkpool':
            Pool(),
        })
        with requests.session() as s:
            if params['url'].endswith(
                    '.m3u8'):  # AceEngine return link for HLS stream
                import m3u8
                urls = []
                while checkBroadcast():
                    for url in m3u8.load(params['url']).segments.uri:
                        if url in urls: continue
                        else:
                            StreamWriter(url)
                            urls.append(url)
                            if len(urls) > 50: urls.pop(0)

            else:
                StreamWriter(
                    params['url'])  #AceStream return link for HTTP stream
    except (TypeError, gevent.GreenletExit):
        pass
    except Exception as err:
        _ = AceProxy.pool.map(
            lambda x: x.send_error(500, repr(err), logging.ERROR),
            params['broadcastclients'])
Пример #3
0
    def handle(self, connection, headers_only=False):
        play = False
        # 30 minutes cache
        if not self.playlist or (gevent.time.time() - self.playlisttime > 30 * 60):
           if not self.Playlistparser(): connection.dieWithError(); return

        url = urlparse(connection.path)
        path = url.path[0:-1] if url.path.endswith('/') else url.path
        params = parse_qs(connection.query)

        if path.startswith('/%s/channel/' % connection.reqtype):
           if not path.endswith('.ts'):
              connection.dieWithError(404, 'Invalid path: %s' % unquote(path), logging.ERROR)
              return
           name = ensure_text(unquote(path[path.rfind('/')+1:]))
           url = self.channels.get('.'.join(name.split('.')[:-1]))
           if url is None:
              connection.dieWithError(404, 'Unknown channel: ' + name, logging.ERROR)
              return
           elif url.startswith('acestream://'):
              connection.path = '/content_id/%s/%s' % (url.split('/')[2], name)
           elif url.startswith('infohash://'):
              connection.path = '/infohash/%s/%s' % (url.split('/')[2], name)
           elif url.startswith(('http://', 'https://')) and url.endswith(('.acelive', '.acestream', '.acemedia', '.torrent')):
              connection.path = '/url/%s/%s' % (quote(url,''), name)
           connection.splittedpath = connection.path.split('/')
           connection.reqtype = connection.splittedpath[1].lower()
           name = name.split('.')[0]
           play = True
        elif self.etag == connection.headers.get('If-None-Match'):
           self.logger.debug('ETag matches - returning 304')
           connection.send_response(304)
           connection.send_header('Connection', 'close')
           connection.end_headers()
           return
        else:
           hostport = connection.headers['Host']
           path = '' if not self.channels else '/%s/channel' % connection.reqtype
           add_ts = True if path.endswith('/ts') else False
           exported = self.playlist.exportm3u(hostport=hostport, path=path, add_ts=add_ts, header=config.m3uheadertemplate, fmt=params.get('fmt', [''])[0])
           response_headers = { 'Content-Type': 'audio/mpegurl; charset=utf-8', 'Connection': 'close', 'Content-Length': len(exported),
                                'Access-Control-Allow-Origin': '*', 'ETag': self.etag }
           try:
              h = connection.headers.get('Accept-Encoding').split(',')[0]
              compress_method = { 'zlib': zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS),
                                  'deflate': zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS),
                                  'gzip': zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16) }
              exported = compress_method[h].compress(exported) + compress_method[h].flush()
              response_headers['Content-Length'] = len(exported)
              response_headers['Content-Encoding'] = h
           except: pass

           connection.send_response(200)
           gevent.joinall([gevent.spawn(connection.send_header, k, v) for (k,v) in response_headers.items()])
           connection.end_headers()

        if play: connection.handleRequest(headers_only, name, self.picons.get(name), fmt=params.get('fmt', [''])[0])
        elif not headers_only:
           self.logger.debug('Exporting torrent-telik.m3u playlist')
           connection.wfile.write(exported)
Пример #4
0
def StreamReader(playback_url, cid):
    def write_chunk(client, data, timeout=15.0, _bytearray=bytearray):
        try:
            client.q.put(_bytearray('%x\r\n' % len(data), 'utf-8') + data +
                         b'\r\n' if client.response_use_chunked else data,
                         timeout=timeout)
        except:  # Client did not read the data from socket for N sec - disconnect it
            logging.warning('Client %s does not read data until %s sec' %
                            (client.clientip, timeout))
            client.finish()

    def StreamWriter(url):
        for chunk in s.get(url,
                           timeout=(5, AceConfig.videotimeout)).iter_content(
                               chunk_size=1048576):
            clients = AceProxy.clientcounter.getClientsList(cid)
            if not clients: break
            gevent.joinall([
                gevent.spawn(write_chunk, client, chunk) for client in clients
                if chunk
            ])

    try:
        playback_url = urlparse(playback_url)._replace(
            netloc='{aceHostIP}:{aceHTTPport}'.format(
                **AceConfig.ace)).geturl()
        with requests.session() as s:
            s.verify = False
            s.stream = True
            if playback_url.endswith(
                    '.m3u8'):  # AceEngine return link for HLS stream
                used_urls = []
                while 1:
                    for url in s.get(
                            playback_url,
                            timeout=(5, AceConfig.videotimeout)).iter_lines():
                        if not AceProxy.clientcounter.getClientsList(
                                cid) or url.startswith(b'download not found'):
                            return
                        if url.startswith(b'http://') and url not in used_urls:
                            StreamWriter(url)
                            used_urls.append(url)
                            if len(used_urls) > 15: used_urls.pop(0)
            else:
                StreamWriter(
                    playback_url)  #AceStream return link for HTTP stream
    except TypeError:
        pass
    except Exception as err:
        clients = AceProxy.clientcounter.getClientsList(cid)
        gevent.joinall([
            gevent.spawn(client.dieWithError, 500, repr(err), logging.ERROR)
            for client in clients
        ])
        gevent.joinall(
            [gevent.spawn(client.handlerGreenlet.kill) for client in clients])
Пример #5
0
    def do_GET(self, headers_only=False):
        '''
        GET request handler
        '''
        # Current greenlet
        self.handlerGreenlet = gevent.getcurrent()
        # Connected client IP address
        self.clientip = self.headers[
            'X-Forwarded-For'] if 'X-Forwarded-For' in self.headers else self.client_address[
                0]
        logging.info('Accepted connection from %s path %s' %
                     (self.clientip, unquote(self.path)))
        logging.debug('Client headers: %s' % dict(self.headers))
        params = urlparse(self.path)
        self.query, self.path = params.query, params.path[:
                                                          -1] if params.path.endswith(
                                                              '/'
                                                          ) else params.path

        if AceConfig.firewall and not checkFirewall(self.clientip):
            self.dieWithError(
                401, 'Dropping connection from %s due to firewall rules' %
                self.clientip, logging.ERROR)
            return
        try:
            self.splittedpath = self.path.split('/')
            self.reqtype = self.splittedpath[1].lower()
            # backward compatibility
            old2newUrlParts = {'torrent': 'url', 'pid': 'content_id'}
            if self.reqtype in old2newUrlParts:
                self.reqtype = old2newUrlParts[self.reqtype]

            # If first parameter is 'content_id','url','infohash' .... etc or it should be handled by plugin #'direct_url', 'data', 'efile_url'
            if not (self.reqtype in ('content_id', 'url', 'infohash')
                    or self.reqtype in AceProxy.pluginshandlers):
                self.dieWithError(400, 'Bad Request',
                                  logging.WARNING)  # 400 Bad Request
                return
        except IndexError:
            self.dieWithError(400, 'Bad Request',
                              logging.WARNING)  # 400 Bad Request
            return

        # Handle request with plugin handler
        if self.reqtype in AceProxy.pluginshandlers:
            try:
                AceProxy.pluginshandlers.get(self.reqtype).handle(
                    self, headers_only)
            except Exception as e:
                import traceback
                logger.error(traceback.format_exc())
                self.dieWithError(500, 'Plugin exception: %s' % repr(e))
            finally:
                return
        self.handleRequest(headers_only)
Пример #6
0
def StreamReader(playback_url, cid):
    def write_chunk(client, data, timeout=15.0, _bytearray=bytearray):
        try:
            client.q.put(_bytearray('%x\r\n' % len(data), 'utf-8') + data +
                         b'\r\n' if client.response_use_chunked else data,
                         timeout=timeout)
        except gevent.queue.Full:
            client.dieWithError(
                500, 'Client %s does not read data until %s sec' %
                (client.clientip, timeout), logging.ERROR)

    def StreamWriter(url):
        for chunk in s.get(url,
                           timeout=(5, AceConfig.videotimeout)).iter_content(
                               chunk_size=1048576):
            AceProxy.pool.map(lambda x: write_chunk(x, chunk),
                              AceProxy.clientcounter.getClientsList(cid))

    try:
        playback_url = urlparse(playback_url)._replace(
            netloc='{aceHostIP}:{aceHTTPport}'.format(
                **AceConfig.ace)).geturl()
        with requests.session() as s:
            s.verify = False
            s.stream = True
            if playback_url.endswith(
                    '.m3u8'):  # AceEngine return link for HLS stream
                used_urls = []
                while 1:
                    for url in s.get(
                            playback_url,
                            timeout=(5, AceConfig.videotimeout)).iter_lines():
                        if not AceProxy.clientcounter.getClientsList(
                                cid) or url.startswith(b'download not found'):
                            return
                        if url.startswith(b'http://') and url not in used_urls:
                            StreamWriter(url)
                            used_urls.append(url)
                            if len(used_urls) > 15: used_urls.pop(0)
            else:
                StreamWriter(
                    playback_url)  #AceStream return link for HTTP stream
    except TypeError:
        pass
    except Exception as err:
        AceProxy.pool.map(
            lambda x: x.dieWithError(500, repr(err), logging.ERROR),
            AceProxy.clientcounter.getClientsList(cid))
Пример #7
0
    def send(self, request, **kwargs):
        """ Wraps a file, described in request, in a Response object.
            :param request: The PreparedRequest` being "sent".
            :returns: a Response object containing the file
        """

        # Check that the method makes sense. Only support GET
        if request.method not in ("GET", "HEAD"):
            raise ValueError("Invalid request method %s" % request.method)

        # Parse the URL
        url_parts = urlparse(request.url)

        # Reject URLs with a hostname component
        if url_parts.netloc and url_parts.netloc != "localhost":
            raise ValueError(
                "file: URLs with hostname components are not permitted")

        resp = Response()

        # Open the file, translate certain errors into HTTP responses
        # Use urllib's unquote to translate percent escapes into whatever
        # they actually need to be
        try:
            # Split the path on / (the URL directory separator) and decode any
            # % escapes in the parts
            path_parts = [unquote(p) for p in url_parts.path.split('/')]

            # Strip out the leading empty parts created from the leading /'s
            while path_parts and not path_parts[0]:
                path_parts.pop(0)

            # If os.sep is in any of the parts, someone fed us some shenanigans.
            # Treat is like a missing file.
            if any(os.sep in p for p in path_parts):
                raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))

            # Look for a drive component. If one is present, store it separately
            # so that a directory separator can correctly be added to the real
            # path, and remove any empty path parts between the drive and the path.
            # Assume that a part ending with : or | (legacy) is a drive.
            if path_parts and (path_parts[0].endswith('|')
                               or path_parts[0].endswith(':')):
                path_drive = path_parts.pop(0)
                if path_drive.endswith('|'):
                    path_drive = path_drive[:-1] + ':'

                while path_parts and not path_parts[0]:
                    path_parts.pop(0)
            else:
                path_drive = ''

            # Try to put the path back together
            # Join the drive back in, and stick os.sep in front of the path to
            # make it absolute.
            path = path_drive + os.sep + os.path.join(*path_parts)

            # Check if the drive assumptions above were correct. If path_drive
            # is set, and os.path.splitdrive does not return a drive, it wasn't
            # reall a drive. Put the path together again treating path_drive
            # as a normal path component.
            if path_drive and not os.path.splitdrive(path):
                path = os.sep + os.path.join(path_drive, *path_parts)

            # Use io.open since we need to add a release_conn method, and
            # methods can't be added to file objects in python 2.
            resp.raw = io.open(path, "rb")
            resp.raw.release_conn = resp.raw.close
        except IOError as e:
            if e.errno == errno.EACCES:
                resp.status_code = codes.forbidden
            elif e.errno == errno.ENOENT:
                resp.status_code = codes.not_found
            else:
                resp.status_code = codes.bad_request

            # Wrap the error message in a file-like object
            # The error message will be localized, try to convert the string
            # representation of the exception into a byte stream
            resp_str = str(e).encode(locale.getpreferredencoding(False))
            resp.raw = BytesIO(resp_str)
            resp.headers['Content-Length'] = len(resp_str)

            # Add release_conn to the BytesIO object
            resp.raw.release_conn = resp.raw.close
        else:
            resp.status_code = codes.ok
            resp.url = request.url

            # If it's a regular file, set the Content-Length
            resp_stat = os.fstat(resp.raw.fileno())
            if stat.S_ISREG(resp_stat.st_mode):
                resp.headers['Content-Length'] = resp_stat.st_size

        return resp
Пример #8
0
    def handleRequest(self,
                      headers_only,
                      channelName=None,
                      channelIcon=None,
                      fmt=None):
        logger = logging.getLogger('HandleRequest')
        self.reqparams, self.path = parse_qs(
            self.query), self.path[:-1] if self.path.endswith(
                '/') else self.path

        self.videoextdefaults = ('.3gp', '.aac', '.ape', '.asf', '.avi', '.dv',
                                 '.divx', '.flac', '.flc', '.flv', '.m2ts',
                                 '.m4a', '.mka', '.mkv', '.mpeg', '.mpeg4',
                                 '.mpegts', '.mpg4', '.mp3', '.mp4', '.mpg',
                                 '.mov', '.m4v', '.ogg', '.ogm', '.ogv',
                                 '.oga', '.ogx', '.qt', '.rm', '.swf', '.ts',
                                 '.vob', '.wmv', '.wav', '.webm')

        # Limit on the number of connected clients
        if 0 < AceConfig.maxconns <= AceProxy.clientcounter.totalClients():
            self.dieWithError(
                501,
                "Maximum client connections reached, can't serve request from %s"
                % self.clientip, logging.ERROR)
            return
        # Check if third parameter exists…/self.reqtype/blablablablabla/video.mpg
        # And if it ends with regular video extension
        try:
            if not self.path.endswith(self.videoextdefaults):
                self.dieWithError(
                    501,
                    'Request seems like valid but no valid video extension was provided',
                    logging.ERROR)
                return
        except IndexError:
            self.dieWithError(400, 'Bad Request',
                              logging.WARNING)  # 400 Bad Request
            return
        # Pretend to work fine with Fake or HEAD request.
        if headers_only or AceConfig.isFakeRequest(self.path, self.query,
                                                   self.headers):
            # Return 200 and exit
            if headers_only:
                logger.debug('Sending headers and closing connection')
            else:
                logger.debug('Fake request - closing connection')
            self.send_response(200)
            self.send_header('Content-Type', 'video/mp2t')
            self.send_header('Connection', 'Close')
            self.end_headers()
            return
        # Check is AceEngine alive
        checkAce()
        # Make dict with parameters
        # [file_indexes, developer_id, affiliate_id, zone_id, stream_id]
        paramsdict = {}.fromkeys(aceclient.acemessages.AceConst.START_PARAMS,
                                 '0')
        for i in range(3, len(self.splittedpath)):
            paramsdict[aceclient.acemessages.AceConst.START_PARAMS[
                i - 3]] = self.splittedpath[i] if self.splittedpath[i].isdigit(
                ) else '0'
        paramsdict[self.reqtype] = unquote(
            self.splittedpath[2])  #self.path_unquoted
        #End parameters dict
        CID = NAME = None
        if not AceConfig.new_api:
            try:
                if not AceProxy.clientcounter.idleAce:
                    logger.debug('Create connection to AceEngine.....')
                    AceProxy.clientcounter.idleAce = aceclient.AceClient(
                        AceProxy.clientcounter, AceConfig.ace,
                        AceConfig.aceconntimeout, AceConfig.aceresulttimeout)
                    AceProxy.clientcounter.idleAce.aceInit(
                        AceConfig.acesex, AceConfig.aceage, AceConfig.acekey,
                        AceConfig.videoseekback, AceConfig.videotimeout)
                CID, NAME = AceProxy.clientcounter.idleAce.GETINFOHASH(
                    self.reqtype, paramsdict[self.reqtype],
                    paramsdict['file_indexes'])
            except aceclient.AceException as e:
                self.dieWithError(503, '%s' % repr(e), logging.ERROR)
                AceProxy.clientcounter.idleAce = None
                return
        else:
            try:
                with requests.session() as s:
                    s.stream = s.verify = False
                    url = 'http://%s:%s/ace/%s' % (
                        AceConfig.ace['aceHostIP'],
                        AceConfig.ace['aceHTTPport'], 'manifest.m3u8'
                        if AceConfig.acestreamtype['output_format'] == 'hls'
                        else 'getstream')
                    params = {
                        'id' if self.reqtype in ('cid', 'content_id') else self.reqtype:
                        paramsdict[self.reqtype],
                        'format':
                        'json',
                        'pid':
                        str(uuid4()),
                        '_idx':
                        paramsdict['file_indexes']
                    }
                    if AceConfig.acestreamtype['output_format'] == 'hls':
                        params.update(AceConfig.acestreamtype)
                        del params['output_format']
                    self.cmd = s.get(
                        url,
                        params=params,
                        timeout=(5, AceConfig.videotimeout)).json()['response']
                    CID = urlparse(self.cmd['playback_url']).path.split('/')[3]
                    url = 'http://%s:%s/server/api' % (
                        AceConfig.ace['aceHostIP'],
                        AceConfig.ace['aceHTTPport'])
                    params = {
                        'method': 'get_media_files',
                        self.reqtype: paramsdict[self.reqtype]
                    }
                    NAME = s.get(url,
                                 params=params,
                                 timeout=(5, AceConfig.aceresulttimeout)).json(
                                 )['result'][paramsdict['file_indexes']]
            except Exception as e:
                self.dieWithError(503, '%s' % repr(e), logging.ERROR)
                return

        self.connectionTime = gevent.time.time()
        self.channelName = NAME if not channelName else channelName
        self.channelIcon = 'http://static.acestream.net/sites/acestream/img/ACE-logo.png' if not channelIcon else channelIcon
        self.clientInfo = self.transcoder = None
        try:
            self.connectGreenlet = gevent.spawn(
                self.connectDetector)  # client disconnection watchdog
            self.out = self.wfile
            # If &fmt transcode key present in request
            fmt = self.reqparams.get('fmt', [''])[0]
            if fmt and AceConfig.osplatform != 'Windows':
                if fmt in AceConfig.transcodecmd:
                    stderr = None if AceConfig.loglevel == logging.DEBUG else DEVNULL
                    popen_params = {
                        'bufsize': 1048576,
                        'stdin': gevent.subprocess.PIPE,
                        'stdout': self.wfile,
                        'stderr': stderr,
                        'shell': False
                    }
                    try:
                        self.transcoder = gevent.event.AsyncResult()
                        gevent.spawn(lambda: psutil.Popen(
                            AceConfig.transcodecmd[fmt], **popen_params)).link(
                                self.transcoder)
                        self.transcoder = self.transcoder.get(timeout=2.0)
                        self.out = self.transcoder.stdin
                        logger.info('Transcoding for %s started' %
                                    self.clientip)
                    except:
                        logger.error(
                            'Error starting transcoding! Is Ffmpeg or VLC installed?'
                        )
                        self.transcoder = None
                        self.out = self.wfile
                else:
                    logger.error(
                        "Can't found fmt key. Transcoding not started!")

            self.response_use_chunked = False if self.transcoder is not None else AceConfig.use_chunked

            if AceProxy.clientcounter.addClient(CID, self) == 1:
                # If there is no existing broadcast we create it
                playback_url = self.cmd[
                    'playback_url'] if AceConfig.new_api else self.ace.START(
                        self.reqtype, paramsdict, AceConfig.acestreamtype)
                if not AceProxy.ace:  #Rewrite host:port for remote AceEngine
                    playback_url = urlparse(playback_url)._replace(
                        netloc='%s:%s' %
                        (AceConfig.ace['aceHostIP'],
                         AceConfig.ace['aceHTTPport'])).geturl()
                gevent.spawn(StreamReader, playback_url, CID)

            # Sending videostream headers to client
            logger.info('Streaming "%s" to %s started' %
                        (self.channelName, self.clientip))
            drop_headers = []
            proxy_headers = {
                'Connection': 'keep-alive',
                'Keep-Alive': 'timeout=15, max=100',
                'Accept-Ranges': 'none',
                'Transfer-Encoding': 'chunked',
                'Content-Type': 'application/octet-stream',
                'Cache-Control': 'max-age=0, no-cache, no-store',
                'Pragma': 'no-cache'
            }

            if not self.response_use_chunked or self.request_version == 'HTTP/1.0':
                self.protocol_version = 'HTTP/1.0'
                proxy_headers['Connection'] = 'Close'
                drop_headers.extend(
                    ['Transfer-Encoding', 'Keep-Alive', 'Cache-Control'])

            response_headers = [(k, v) for (k, v) in proxy_headers.items()
                                if k not in drop_headers]
            self.send_response(200)
            logger.debug('Sending HTTPAceProxy headers to client: %s' %
                         dict(response_headers))
            gevent.joinall([
                gevent.spawn(self.send_header, k, v)
                for (k, v) in response_headers
            ])
            self.end_headers()

            self.connectGreenlet.join(
            )  # Wait until request complite or client disconnected

        except aceclient.AceException as e:
            gevent.joinall([
                gevent.spawn(client.dieWithError, 503, '%s' % repr(e),
                             logging.ERROR)
                for client in AceProxy.clientcounter.getClientsList(CID)
            ])
            gevent.joinall([
                gevent.spawn(client.connectGreenlet.kill)
                for client in AceProxy.clientcounter.getClientsList(CID)
            ])
        except gevent.GreenletExit:
            pass  # Client disconnected
        except Exception as e:
            self.dieWithError(500, 'Unexpected error: %s' % repr(e))
        finally:
            logging.info('Streaming "%s" to %s finished' %
                         (self.channelName, self.clientip))
            if self.transcoder:
                try:
                    self.transcoder.kill()
                    logging.info('Transcoding for %s stoped' % self.clientip)
                except:
                    pass
            if AceProxy.clientcounter.deleteClient(CID, self) == 0:
                if AceConfig.new_api:
                    with requests.get(self.cmd['command_url'],
                                      params={'method': 'stop'},
                                      timeout=5) as r:
                        logging.debug('Stop broadcast: %s' % r.json())
                logging.debug(
                    'Broadcast "%s" stoped. Last client %s disconnected' %
                    (self.channelName, self.clientip))
Пример #9
0
    def handle(self, connection, headers_only=False):
        play = False
        # 30 minutes cache
        if not self.playlist or (int(gevent.time.time()) - self.playlisttime >
                                 30 * 60):
            self.updatelogos = p2pconfig.email != 're.place@me' and p2pconfig.password != 'ReplaceMe'
            if not self.downloadPlaylist():
                connection.dieWithError()
                return

        url = urlparse(connection.path)
        path = url.path[0:-1] if url.path.endswith('/') else url.path
        params = parse_qs(connection.query)

        if path.startswith('/torrenttv/channel/'):
            name = path.rsplit('.', 1)
            if not name[1]:
                connection.dieWithError(404,
                                        'Invalid path: %s' % unquote(path),
                                        logging.ERROR)
                return
            name = unquote(name[0].rsplit('/', 1)[1])
            if isinstance(name, bytes): name = name.decode('utf-8')  # PY2
            url = self.channels.get(name, None)
            if url is None:
                connection.dieWithError(404, 'Unknown channel: ' + name,
                                        logging.ERROR)
                return
            elif url.startswith('acestream://'):
                connection.path = '/content_id/%s/stream.ts' % url.split(
                    '/')[2]
            elif url.startswith('infohash://'):
                connection.path = '/infohash/%s/stream.ts' % url.split('/')[2]
            elif url.startswith(('http://', 'https://')) and url.endswith(
                ('.acelive', '.acestream', '.acemedia')):
                connection.path = '/url/%s/stream.ts' % quote(url, '')
            connection.splittedpath = connection.path.split('/')
            connection.reqtype = connection.splittedpath[1].lower()
            play = True
        elif self.etag == connection.headers.get('If-None-Match'):
            self.logger.debug('ETag matches - returning 304')
            connection.send_response(304)
            connection.send_header('Connection', 'close')
            connection.end_headers()
            return
        else:
            hostport = connection.headers['Host']
            path = '' if len(self.channels) == 0 else '/torrenttv/channel'
            add_ts = True if path.endswith('/ts') else False
            exported = self.playlist.exportm3u(
                hostport=hostport,
                path=path,
                add_ts=add_ts,
                header=config.m3uheadertemplate,
                fmt=params.get('fmt', [''])[0]).encode('utf-8')
            response_headers = {
                'Content-Type': 'audio/mpegurl; charset=utf-8',
                'Connection': 'close',
                'Content-Length': len(exported),
                'Access-Control-Allow-Origin': '*',
                'ETag': self.etag
            }
            try:
                h = connection.headers.get('Accept-Encoding').split(',')[0]
                compress_method = {
                    'zlib': zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS),
                    'deflate': zlib.compressobj(9, zlib.DEFLATED,
                                                -zlib.MAX_WBITS),
                    'gzip': zlib.compressobj(9, zlib.DEFLATED,
                                             zlib.MAX_WBITS | 16)
                }
                exported = compress_method[h].compress(
                    exported) + compress_method[h].flush()
                response_headers['Content-Length'] = len(exported)
                response_headers['Content-Encoding'] = h
            except:
                pass

            connection.send_response(200)
            gevent.joinall([
                gevent.spawn(connection.send_header, k, v)
                for (k, v) in response_headers.items()
            ])
            connection.end_headers()

        if play:
            connection.handleRequest(headers_only,
                                     name,
                                     self.logomap.get(name),
                                     fmt=params.get('fmt', [''])[0])
        elif not headers_only:
            self.logger.debug('Exporting m3u playlist')
            connection.wfile.write(exported)