def handle(self, fmt=None): logger = logging.getLogger("ClientHandler") if not self.ace._state.wait(timeout=5.0): # STATE 1 (PREBUFFERING) self.handler.dieWithError(500, 'Video stream not opened in 5sec - disconnecting') return self.connectionTime = time.time() remaining = self.connectionTime + AceConfig.videostartbuffertime while remaining >= time.time(): gevent.sleep() if self.queue.qsize() >= self.ace._streamReaderQueue.maxsize: break # Sending videostream headers to client if self.handler.connection: response_headers = {'Connection': 'Keep-Alive', 'Keep-Alive': 'timeout=15, max=100', 'Content-Type': 'application/octet-stream', 'Access-Control-Allow-Origin': '*'} self.handler.send_response(200) logger.debug('Sending HTTPAceProxy headers to client: %s' % response_headers) for k,v in list(response_headers.items()): self.handler.send_header(k,v) self.handler.end_headers() transcoder = None out = self.handler.wfile if fmt and AceConfig.osplatform != 'Windows': if fmt in AceConfig.transcodecmd: stderr = None if AceConfig.loglevel == logging.DEBUG else DEVNULL popen_params = { 'bufsize': requests.models.CONTENT_CHUNK_SIZE, 'stdin' : gevent.subprocess.PIPE, 'stdout' : self.handler.wfile, 'stderr' : stderr, 'shell' : False } transcoder = gevent.subprocess.Popen(AceConfig.transcodecmd[fmt], **popen_params) out = transcoder.stdin logger.warning('Ffmpeg transcoding started') else: logger.error("Can't found fmt key. Ffmpeg transcoding not started!") logger.info('Streaming "%s" to %s started. Start buffer size: %s' % \ (self.channelName, self.handler.clientip, AceConfig.bytes2human(self.queue.qsize()*requests.models.CONTENT_CHUNK_SIZE))) while self.handler.connection: gevent.sleep() try: out.write(self.queue.get(timeout=AceConfig.videotimeout)) except gevent.queue.Empty: logger.warning('No data received from StreamReader for %ssec - disconnecting "%s"' % (AceConfig.videotimeout,self.channelName)) break except: break if transcoder is not None: try: transcoder.kill(); logger.warning('Ffmpeg transcoding stoped') except: pass self.destroy() return
def do_GET(self): ''' GET request handler ''' self.handlerGreenlet = gevent.getcurrent() # Current greenlet self.clientip = self.headers.get('X-Forwarded-For', self.address_string()) # Connected client IP address logging.info(unquote('[{clientip}]: {command} {request_version} request for: {path}'.format(**self.__dict__))) logging.debug('[%s]: Request headers: %s' % (self.clientip, dict(self.headers))) if AceConfig.firewall and not checkFirewall(self.clientip): self.send_error(401, '[{clientip}]: Dropping connection due to firewall rules'.format(**self.__dict__), logging.ERROR) self.path, _, self.query = self.path.partition('?') self.path = self.path.rstrip('/') # Pretend to work fine with Fake or HEAD request. if self.command == 'HEAD' or AceConfig.isFakeRequest(self.path, self.query, self.headers): # Return 200 and exit if self.command != 'HEAD': self.command = 'FAKE' logging.debug('[{clientip}]: {command} request: send headers and close the connection'.format(**self.__dict__)) self.send_response(200) self.send_header('Content-Type', 'video/mp2t') self.send_header('Connection', 'Close') self.end_headers() return try: self.splittedpath = self.path.split('/') self.reqtype = self.splittedpath[1].lower() AceProxy.pluginshandlers[self.reqtype].handle(self) # If request should be handled by plugin raise IndexError() except (IndexError, KeyError): self.reqtype = {'torrent': 'url', 'pid': 'content_id'}.get(self.reqtype, self.reqtype) # For backward compatibility if self.reqtype in {'content_id', 'url', 'infohash', 'direct_url', 'data', 'efile_url'}: # Limit on the number of connected clients if 0 < AceConfig.maxconns <= len(AceProxy.clientcounter.getAllClientsList()): self.send_error(403, "[{clientip}]: Maximum client connections reached, can't serve request".format(**self.__dict__), logging.ERROR) # Check if third path parameter is exists /{reqtype}/{reqtype_value}/.../.../video.mpg # |_________| # And if it ends with regular video extension if not self.splittedpath[-1].endswith(('.avi', '.flv', '.m2ts', '.mkv', '.mpeg', '.mpeg4', '.mpegts', '.mpg4', '.mp4', '.mpg', '.mov', '.mpv', '.qt', '.ts', '.wmv')): self.send_error(501, '[{clientip}]: request seems like valid but no valid video extension was provided'.format(**self.__dict__), logging.ERROR) else: self.handleRequest() elif self.reqtype not in AceProxy.pluginshandlers: self.send_error(400, '[{clientip}]: Bad Request'.format(**self.__dict__), logging.WARNING) # 400 Bad Request except Exception as e: logging.error('Unexpected exception: %s' % repr(e)) logging.error(traceback.format_exc())
def handleRequest(self, headers_only, channelName=None, channelIcon=None, fmt=None): logger = logging.getLogger('handleRequest') logger.debug("Headers:\n" + str(self.headers)) self.requrl = urlparse.urlparse(self.path) self.reqparams = urlparse.parse_qs(self.requrl.query) self.path = self.requrl.path[:-1] if self.requrl.path.endswith( '/') else self.requrl.path # Check if third parameter exists # …/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith( ('.3gp', '.avi', '.flv', '.mkv', '.mov', '.mp4', '.mpeg', '.mpg', '.ogv', '.ts')): logger.error( "Request seems like valid but no valid video extension was provided" ) self.dieWithError(400) return except IndexError: self.dieWithError(400) # 400 Bad Request return # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: logger.debug("Maximum connections reached, can't serve this") self.dieWithError(503) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg") self.end_headers() self.closeConnection() return # Make list with parameters self.params = list() for i in xrange(3, 8): try: self.params.append(int(self.splittedpath[i])) except (IndexError, ValueError): self.params.append('0') self.url = None self.video = None self.path_unquoted = urllib2.unquote(self.splittedpath[2]) contentid = self.getCid(self.reqtype, self.path_unquoted) cid = contentid if contentid else self.path_unquoted logger.debug("CID: " + cid) self.client = Client(cid, self, channelName, channelIcon) self.vlcid = urllib2.quote(cid, '') shouldStart = AceStuff.clientcounter.add(cid, self.client) == 1 try: # Initializing AceClient if shouldStart: if contentid: self.client.ace.START('PID', {'content_id': contentid}) elif self.reqtype == 'pid': self.client.ace.START( self.reqtype, { 'content_id': self.path_unquoted, 'file_indexes': self.params[0] }) elif self.reqtype == 'torrent': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_TORRENT, self.params)) paramsdict['url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) logger.debug("START done") # Getting URL self.url = self.client.ace.getUrl(AceConfig.videotimeout) # Rewriting host for remote Ace Stream Engine self.url = self.url.replace('127.0.0.1', AceConfig.acehost) self.errorhappened = False if shouldStart: logger.debug("Got url " + self.url) # If using VLC, add this url to VLC if AceConfig.vlcuse: # Force ffmpeg demuxing if set in config if AceConfig.vlcforceffmpeg: self.vlcprefix = 'http/ffmpeg://' else: self.vlcprefix = '' self.client.ace.pause() # Sleeping videodelay gevent.sleep(AceConfig.videodelay) self.client.ace.play() AceStuff.vlcclient.startBroadcast( self.vlcid, self.vlcprefix + self.url, AceConfig.vlcmux, AceConfig.vlcpreaccess) # Sleep a bit, because sometimes VLC doesn't open port in # time gevent.sleep(0.5) self.hanggreenlet = gevent.spawn(self.hangDetector) logger.debug("hangDetector spawned") gevent.sleep() # Building new VLC url if AceConfig.vlcuse: self.url = 'http://' + AceConfig.vlchost + \ ':' + str(AceConfig.vlcoutport) + '/' + self.vlcid logger.debug("VLC url " + self.url) # Sending client headers to videostream self.video = urllib2.Request(self.url) for key in self.headers.dict: self.video.add_header(key, self.headers.dict[key]) self.video = urllib2.urlopen(self.video) # Sending videostream headers to client if not self.headerssent: self.send_response(self.video.getcode()) if self.video.info().dict.has_key('connection'): del self.video.info().dict['connection'] if self.video.info().dict.has_key('server'): del self.video.info().dict['server'] if self.video.info().dict.has_key('transfer-encoding'): del self.video.info().dict['transfer-encoding'] if self.video.info().dict.has_key('keep-alive'): del self.video.info().dict['keep-alive'] for key in self.video.info().dict: self.send_header(key, self.video.info().dict[key]) # End headers. Next goes video data self.end_headers() logger.debug("Headers sent") # Run proxyReadWrite self.proxyReadWrite() else: if not fmt: fmt = self.reqparams.get( 'fmt')[0] if self.reqparams.has_key('fmt') else None self.client.handle(shouldStart, self.url, fmt) except (aceclient.AceException, vlcclient.VlcException, urllib2.URLError) as e: logger.error("Exception: " + repr(e)) self.errorhappened = True self.dieWithError() except gevent.GreenletExit: # hangDetector told us about client disconnection pass except Exception: # Unknown exception logger.error(traceback.format_exc()) self.errorhappened = True self.dieWithError() finally: if AceConfig.videodestroydelay and not self.errorhappened and AceStuff.clientcounter.count( cid) == 1: # If no error happened and we are the only client try: logger.debug("Sleeping for " + str(AceConfig.videodestroydelay) + " seconds") gevent.sleep(AceConfig.videodestroydelay) except: pass try: remaining = AceStuff.clientcounter.delete(cid, self.client) self.client.destroy() self.ace = None self.client = None if AceConfig.vlcuse and remaining == 0: try: AceStuff.vlcclient.stopBroadcast(self.vlcid) except: pass logger.debug("END REQUEST") except: logger.error(traceback.format_exc())
def handleRequest(self, headers_only, channelName=None, channelIcon=None, fmt=None): logger = logging.getLogger('handleRequest') logger.debug("Headers:\n" + str(self.headers)) self.requrl = urlparse.urlparse(self.path) self.reqparams = urlparse.parse_qs(self.requrl.query) self.path = self.requrl.path[:-1] if self.requrl.path.endswith('/') else self.requrl.path # Check if third parameter exists # …/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith(('.3gp', '.avi', '.flv', '.mkv', '.mov', '.mp4', '.mpeg', '.mpg', '.ogv', '.ts')): logger.error("Request seems like valid but no valid video extension was provided") self.dieWithError(400) return except IndexError: self.dieWithError(400) # 400 Bad Request return # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: logger.debug("Maximum connections reached, can't serve this") self.dieWithError(503) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg") self.end_headers() self.closeConnection() return # Make list with parameters self.params = list() for i in xrange(3, 8): try: self.params.append(int(self.splittedpath[i])) except (IndexError, ValueError): self.params.append('0') self.url = None self.video = None self.path_unquoted = urllib2.unquote(self.splittedpath[2]) contentid = self.getCid(self.reqtype, self.path_unquoted) cid = contentid if contentid else self.path_unquoted logger.debug("CID: " + cid) self.client = Client(cid, self, channelName, channelIcon) self.vlcid = urllib2.quote(cid, '') shouldStart = AceStuff.clientcounter.add(cid, self.client) == 1 try: # Initializing AceClient if shouldStart: if contentid: self.client.ace.START('PID', {'content_id': contentid}) elif self.reqtype == 'pid': self.client.ace.START( self.reqtype, {'content_id': self.path_unquoted, 'file_indexes': self.params[0]}) elif self.reqtype == 'torrent': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_TORRENT, self.params)) paramsdict['url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) logger.debug("START done") # Getting URL self.url = self.client.ace.getUrl(AceConfig.videotimeout) # Rewriting host for remote Ace Stream Engine self.url = self.url.replace('127.0.0.1', AceConfig.acehost) self.errorhappened = False if shouldStart: logger.debug("Got url " + self.url) # If using VLC, add this url to VLC if AceConfig.vlcuse: # Force ffmpeg demuxing if set in config if AceConfig.vlcforceffmpeg: self.vlcprefix = 'http/ffmpeg://' else: self.vlcprefix = '' self.client.ace.pause() # Sleeping videodelay gevent.sleep(AceConfig.videodelay) self.client.ace.play() AceStuff.vlcclient.startBroadcast( self.vlcid, self.vlcprefix + self.url, AceConfig.vlcmux, AceConfig.vlcpreaccess) # Sleep a bit, because sometimes VLC doesn't open port in # time gevent.sleep(0.5) self.hanggreenlet = gevent.spawn(self.hangDetector) logger.debug("hangDetector spawned") gevent.sleep() # Building new VLC url if AceConfig.vlcuse: self.url = 'http://' + AceConfig.vlchost + \ ':' + str(AceConfig.vlcoutport) + '/' + self.vlcid logger.debug("VLC url " + self.url) # Sending client headers to videostream self.video = urllib2.Request(self.url) for key in self.headers.dict: self.video.add_header(key, self.headers.dict[key]) self.video = urllib2.urlopen(self.video) # Sending videostream headers to client if not self.headerssent: self.send_response(self.video.getcode()) if self.video.info().dict.has_key('connection'): del self.video.info().dict['connection'] if self.video.info().dict.has_key('server'): del self.video.info().dict['server'] if self.video.info().dict.has_key('transfer-encoding'): del self.video.info().dict['transfer-encoding'] if self.video.info().dict.has_key('keep-alive'): del self.video.info().dict['keep-alive'] for key in self.video.info().dict: self.send_header(key, self.video.info().dict[key]) # End headers. Next goes video data self.end_headers() logger.debug("Headers sent") # Run proxyReadWrite self.proxyReadWrite() else: if not fmt: fmt = self.reqparams.get('fmt')[0] if self.reqparams.has_key('fmt') else None self.client.handle(shouldStart, self.url, fmt) except (aceclient.AceException, vlcclient.VlcException, urllib2.URLError) as e: logger.error("Exception: " + repr(e)) self.errorhappened = True self.dieWithError() except gevent.GreenletExit: # hangDetector told us about client disconnection pass except Exception: # Unknown exception logger.error(traceback.format_exc()) self.errorhappened = True self.dieWithError() finally: if AceConfig.videodestroydelay and not self.errorhappened and AceStuff.clientcounter.count(cid) == 1: # If no error happened and we are the only client try: logger.debug("Sleeping for " + str(AceConfig.videodestroydelay) + " seconds") gevent.sleep(AceConfig.videodestroydelay) except: pass try: remaining = AceStuff.clientcounter.delete(cid, self.client) self.client.destroy() self.ace = None self.client = None if AceConfig.vlcuse and remaining == 0: try: AceStuff.vlcclient.stopBroadcast(self.vlcid) except: pass logger.debug("END REQUEST") except: logger.error(traceback.format_exc())
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))
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') # If firewall enabled if AceConfig.firewall and not checkFirewall(self.clientip): self.dieWithError(403, 'Dropping connection from %s due to firewall rules' % self.clientip, logging.ERROR) # 403 Forbidden 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(400, 'Request seems like valid but no valid video extension was provided', logging.ERROR) return except IndexError: self.dieWithError(400, 'Bad Request', logging.WARNING); return # 400 Bad Request # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: self.dieWithError(503, "Maximum client connections reached, can't serve request from %" % self.clientip, logging.ERROR) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg') self.send_header('Connection', 'Close') self.end_headers() return # 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] = requests.compat.unquote(self.splittedpath[2]) #self.path_unquoted #End parameters dict self.client = None try: CID, NAME = self.getINFOHASH(self.reqtype, paramsdict[self.reqtype], paramsdict['file_indexes']) if not channelName: channelName = NAME if not channelIcon: channelIcon = 'http://static.acestream.net/sites/acestream/img/ACE-logo.png' # Create client self.client = Client(self, CID, channelName, channelIcon) # If there is no existing broadcast we create it if AceStuff.clientcounter.add(CID, self.client) == 1: logger.warning('Create a broadcast "%s"' % self.client.channelName) # Send START commands to AceEngine and Getting URL from engine url = self.client.ace.START(self.reqtype, paramsdict, AceConfig.acestreamtype) # Rewriting host:port for remote Ace Stream Engine if not AceStuff.ace: url = requests.compat.urlparse(url)._replace(netloc='%s:%s' % (AceConfig.ace['aceHostIP'], AceConfig.ace['aceHTTPport'])).geturl() # Start streamreader for broadcast gevent.spawn(self.client.ace.StreamReader, url, CID, AceStuff.clientcounter) logger.warning('Broadcast "%s" created' % self.client.channelName) except aceclient.AceException as e: self.dieWithError(500, 'AceClient exception: %s' % repr(e)) except Exception as e: self.dieWithError(500, 'Unkonwn exception: %s' % repr(e)) else: # streaming to client self.client.handle(self.reqparams.get('fmt', [''])[0]) logger.info('Streaming "%s" to %s finished' % (self.client.channelName, self.clientip)) finally: if self.client and AceStuff.clientcounter.delete(CID, self.client) == 0: logger.warning('Broadcast "%s" stoped. Last client disconnected' % self.client.channelName) return
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 videoextdefaults = ('.avi', '.flv', '.m2ts', '.mkv', '.mpeg', '.mpeg4', '.mpegts', '.mpg4', '.mp4', '.mpg', '.mov', '.mpv', '.qt', '.ts', '.wmv') # Limit on the number of connected clients if 0 < AceConfig.maxconns <= len( AceProxy.clientcounter.getAllClientsList()): self.dieWithError( 403, "Maximum client connections reached, can't serve request from %s" % self.clientip, logging.ERROR) return # Check if third parameter exists…/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith(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() self.closeConnection() return # 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 self.connectionTime = gevent.time.time() self.sessionID = str(uuid4().int)[:8] self.clientInfo = self.transcoder = None self.channelIcon = 'http://static.acestream.net/sites/acestream/img/ACE-logo.png' if channelIcon is None else channelIcon 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) self.CID, self.channelName = AceProxy.clientcounter.idleAce.GETINFOHASH( self.reqtype, paramsdict[self.reqtype], self.sessionID, paramsdict['file_indexes']) except aceclient.AceException as e: AceProxy.clientcounter.idleAce = None self.dieWithError(404, '%s' % repr(e), logging.ERROR) return mimetype = mimetypes.guess_type(self.channelName)[0] try: gevent.spawn(wrap_errors( gevent.socket.error, self.rfile.read)).link( lambda x: self.finish()) # Client disconection watchdog self.q = gevent.queue.Queue(maxsize=AceConfig.videotimeout) self.out = self.wfile # If &fmt transcode key present in request if fmt is None: 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() AceProxy.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!") # Start broadcast if it does not exist if AceProxy.clientcounter.addClient(self) == 1: playback_url = self.ace.START(self.reqtype, paramsdict, AceConfig.acestreamtype) AceProxy.pool.spawn( StreamReader, playback_url, self.CID).link(lambda x: logging.debug( 'Broadcast "%s" stoped. Last client disconnected' % self.channelName)) logger.info('Streaming "%s" to %s started' % (self.channelName, self.clientip)) # Sending videostream headers to client self.response_use_chunked = False if ( self.transcoder is not None or self.request_version == 'HTTP/1.0') else AceConfig.use_chunked drop_headers = [] proxy_headers = { 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=15, max=100', 'Accept-Ranges': 'none', 'Transfer-Encoding': 'chunked', 'Content-Type': 'video/MP2T' if mimetype is None else mimetype, 'Pragma': 'no-cache', 'Cache-Control': 'max-age=0, no-cache, no-store' } if not self.response_use_chunked: 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() # write data to client while he is alive for chunk in self.q: self.out.write(chunk) except aceclient.AceException as e: AceProxy.pool.map( lambda x: x.dieWithError(500, repr(e), logging.ERROR), AceProxy.clientcounter.getClientsList(self.CID)) except (gevent.GreenletExit, gevent.socket.error): pass # Client disconnected finally: AceProxy.clientcounter.deleteClient(self) 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 self.closeConnection()
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 # Limit on the number of clients connected to broadcasts if 0 < AceConfig.maxconns <= AceStuff.clientcounter.totalClients(): self.dieWithError(503, "Maximum client connections reached, can't serve request from %s" % self.clientip, logging.ERROR) # 503 Service Unavailable return 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') # 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(400, 'Request seems like valid but no valid video extension was provided', logging.ERROR) return except IndexError: self.dieWithError(400, 'Bad Request', logging.WARNING); return # 400 Bad Request # 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/mpeg') self.send_header('Connection', 'Close') self.end_headers() return # 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] = requests.compat.unquote(self.splittedpath[2]) #self.path_unquoted #End parameters dict self.connectionTime = gevent.time.time() CID = NAME = None try: if not AceStuff.clientcounter.idleAce: logger.debug('Create connection to AceEngine.....') AceStuff.clientcounter.idleAce = aceclient.AceClient(AceStuff.clientcounter, AceConfig.ace, AceConfig.aceconntimeout, AceConfig.aceresulttimeout) AceStuff.clientcounter.idleAce.aceInit(AceConfig.acesex, AceConfig.aceage, AceConfig.acekey, AceConfig.videoseekback, AceConfig.videotimeout) if self.reqtype not in ('direct_url', 'efile_url'): CID, NAME = AceStuff.clientcounter.idleAce.GETINFOHASH(self.reqtype, paramsdict[self.reqtype], paramsdict['file_indexes']) 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 # If there is no existing broadcast we create it if AceStuff.clientcounter.addClient(CID, self) == 1: logger.warning('Create a broadcast "%s"' % self.channelName) # Send START commands to AceEngine and Getting URL from engine url = self.ace.START(self.reqtype, paramsdict, AceConfig.acestreamtype) # Rewriting host:port for remote Ace Stream Engine if not AceStuff.ace: url = requests.compat.urlparse(url)._replace(netloc='%s:%s' % (AceConfig.ace['aceHostIP'], AceConfig.ace['aceHTTPport'])).geturl() # Start streamreader for broadcast gevent.spawn(self.ace.AceStreamReader, url, CID) #except: pass logger.warning('Broadcast "%s" created' % self.channelName) except aceclient.AceException as e: self.dieWithError(500, 'AceClient exception: %s' % repr(e)) except Exception as e: self.dieWithError(500, 'Unkonwn exception: %s' % repr(e)) else: # streaming to client self.transcoder = None self.out = self.wfile 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 } self.transcoder = gevent.subprocess.Popen(AceConfig.transcodecmd[fmt], **popen_params) self.out = self.transcoder.stdin logger.warning('Ffmpeg transcoding started') else: logger.error("Can't found fmt key. Ffmpeg transcoding not started!") logger.info('Streaming "%s" to %s started' % (self.channelName, self.clientip)) # Sending videostream headers to client headers = { 'Connection': 'Keep-Alive', 'Keep-Alive': 'timeout=15, max=100', 'Accept-Ranges': 'none', 'Content-Type': 'application/octet-stream', 'Transfer-Encoding': 'chunked' } drop_headers = [] if self.transcoder: drop_headers.extend(['Transfer-Encoding']) response_headers = [ (k,v) for (k,v) in headers.items() if k not in drop_headers ] self.send_response(200) logger.debug('Sending HTTPAceProxy headers to client: %s' % dict(response_headers)) for (k,v) in response_headers: self.send_header(k,v) self.end_headers() while self.connection: gevent.sleep(0.5) # Stream data to client from AceStreamReader if self.transcoder is not None: try: self.transcoder.kill(); logger.warning('Ffmpeg transcoding stoped') except: pass logger.info('Streaming "%s" to %s finished' % (self.channelName, self.clientip)) finally: if CID and AceStuff.clientcounter.deleteClient(CID, self) == 0: logger.warning('Broadcast "%s" stoped. Last client disconnected' % self.channelName) return
def handleRequest(self, **params): ''' :params: dict() with keys: headers_only, channelName, channelIcon ''' logger = logging.getLogger('HandleRequest') self.path = self.path[:-1] if self.path.endswith('/') else self.path # Limit on the number of connected clients if 0 < AceConfig.maxconns <= len( AceProxy.clientcounter.getAllClientsList()): self.dieWithError( 403, "Maximum client connections reached, can't serve request from %s" % self.clientip, logging.ERROR) return # Check if third parameter exists…/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith( ('.avi', '.flv', '.m2ts', '.mkv', '.mpeg', '.mpeg4', '.mpegts', '.mpg4', '.mp4', '.mpg', '.mov', '.mpv', '.qt', '.ts', '.wmv')): 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 params.get('headers_only') or AceConfig.isFakeRequest( self.path, self.query, self.headers): # Return 200 and exit if params.get('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() self.closeConnection() return # Make parameters dict params.update({self.reqtype: unquote(self.splittedpath[2])}) # {command: value} params.update( {}.fromkeys(aceclient.acemessages.AceConst.START_PARAMS, '0') ) # [file_indexes, developer_id, affiliate_id, zone_id, stream_id] params.update({ k: v for (k, v) in [(aceclient.acemessages.AceConst.START_PARAMS[i - 3], self.splittedpath[i] if self.splittedpath[i].isdigit() else '0') for i in range(3, len(self.splittedpath))] }) params.update({ 'stream_type': ' '.join([ '{}={}'.format(k, v) for k, v in AceConfig.acestreamtype.items() ]) }) # request http or hls from AceEngine params['request_id'] = self.sessionID = str(uuid4().int)[:8] # End parameters dict self.connectionTime = gevent.time.time() self.clientInfo = None self.channelIcon = params.get('channelIcon') if self.channelIcon is None: self.channelIcon = 'http://static.acestream.net/sites/acestream/img/ACE-logo.png' try: if not AceProxy.clientcounter.idleAce: logger.debug( 'Create connection with AceStream on {aceHostIP}:{aceAPIport}' .format(**AceConfig.ace)) AceProxy.clientcounter.idleAce = aceclient.AceClient( AceConfig.ace, AceConfig.aceconntimeout, AceConfig.aceresulttimeout) AceProxy.clientcounter.idleAce.GetAUTH(AceConfig.acesex, AceConfig.aceage, AceConfig.acekey, AceConfig.videoseekback, AceConfig.videotimeout) if self.reqtype not in ('direct_url', 'efile_url'): self.CID, self.channelName = AceProxy.clientcounter.idleAce.GetCONTENTINFO( params) else: self.channelName = params.get('channelName') if self.channelName is None: self.channelName = 'NoNameChannel' self.CID = requests.auth.hashlib.sha1( self.channelName.encode('utf-8')).hexdigest() except aceclient.AceException as e: AceProxy.clientcounter.idleAce = None self.dieWithError(404, '%s' % repr(e), logging.ERROR) return ext = self.channelName[self.channelName.rfind('.') + 1:] if ext == self.channelName: ext = parse_qs(self.query).get('ext', ['ts'])[0] mimetype = mimetypes.guess_type('%s.%s' % (self.channelName, ext))[0] try: AceProxy.pool.spawn( wrap_errors(gevent.socket.error, self.rfile.read)).link( lambda x: self.finish()) # Client disconection watchdog self.q = gevent.queue.Queue(maxsize=AceConfig.videotimeout) out = self.wfile # If &fmt transcode key present in request fmt = parse_qs(self.query).get('fmt', [''])[0] transcoder = gevent.event.AsyncResult() if fmt: if 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: #transcoder = gevent.event.AsyncResult() AceProxy.pool.spawn(lambda: psutil.Popen( AceConfig.transcodecmd[fmt], **popen_params) ).link(transcoder) out = transcoder.get(timeout=2.0).stdin logger.info( 'Transcoding for {clientip} started'.format( **self.__dict__)) except: logger.error( 'Error starting transcoding! Is Ffmpeg or VLC installed?' ) else: logger.error( "Can't found fmt key. Transcoding not started!") elif AceConfig.osplatform == 'Windows': logger.error( 'Not applicable in Windnows OS. Transcoding not started!' ) # Start broadcast if it does not exist if AceProxy.clientcounter.addClient(self) == 1: START = self.ace.GetBroadcastStartParams(params) self.broadcast = AceProxy.pool.spawn(StreamReader, START['url'], START['infohash']) self.broadcast.link(lambda x: logging.debug( 'Broadcast "{channelName}" stoped. Last client disconnected' .format(**self.__dict__))) logger.info( 'Streaming "{channelName}" to {clientip} started'.format( **self.__dict__)) # Sending videostream headers to client response_use_chunked = False if ( transcoder is not None or self.request_version == 'HTTP/1.0') else AceConfig.use_chunked drop_headers = [] proxy_headers = { 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=%s, max=100' % AceConfig.videotimeout, 'Accept-Ranges': 'none', 'Transfer-Encoding': 'chunked', 'Content-Type': 'video/MP2T' if mimetype is None else mimetype } if not response_use_chunked: self.protocol_version = 'HTTP/1.0' proxy_headers['Connection'] = 'Close' drop_headers.extend(['Transfer-Encoding', 'Keep-Alive']) 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() # write data to client while he is alive for chunk in self.q: out.write(b'%x\r\n' % len(chunk) + chunk + b'\r\n' if response_use_chunked else chunk) except aceclient.AceException as e: _ = AceProxy.pool.map( lambda x: x.dieWithError(500, repr(e), logging.ERROR), AceProxy.clientcounter.getClientsList(self.CID)) except (gevent.GreenletExit, gevent.socket.error): pass # Client disconnected finally: AceProxy.clientcounter.deleteClient(self) logging.info( 'Streaming "{channelName}" to {clientip} finished'.format( **self.__dict__)) if transcoder.value: try: transcoder.value.kill() logging.info('Transcoding for {clientip} stoped'.format( **self.__dict__)) except: pass self.closeConnection() return
def handleRequest(self, headers_only, channelName=None, channelIcon=None, fmt=None): logger = logging.getLogger('HandleRequest') #logger.debug("Accept connected client headers :\n" + str(self.headers)) self.requrl = urlparse.urlparse(self.path) self.reqparams = urlparse.parse_qs(self.requrl.query) self.path = self.requrl.path[:-1] if self.requrl.path.endswith( '/') else self.requrl.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') # Check if third parameter exists…/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith(self.videoextdefaults): logger.error( "Request seems like valid but no valid video extension was provided" ) self.dieWithError(400) return except IndexError: self.dieWithError(400) # 400 Bad Request return # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: logger.info("Maximum connections reached, can't serve this") self.dieWithError(503) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg") self.send_header("Connection", "Close") self.end_headers() self.closeConnection() return # Make list with parameters # file_indexes developer_id affiliate_id zone_id stream_id self.params = list() for i in xrange(3, 8): try: self.params.append(int(self.splittedpath[i])) except (IndexError, ValueError): self.params.append('0') # End parameters self.url = None self.video = None self.path_unquoted = requests.utils.unquote(self.splittedpath[2]) contentid = self.getCid(self.reqtype, self.path_unquoted) cid = contentid if contentid else self.path_unquoted self.vlcid = requests.utils.quote(cid, '') self.client = Client(cid, self, channelName, channelIcon) shouldStart = AceStuff.clientcounter.add(cid, self.client) == 1 try: # Initializing AceClient if shouldStart: # Send commands to AceEngine if contentid: self.client.ace.START('PID', { 'content_id': contentid, 'file_indexes': self.params[0] }) elif self.reqtype == 'pid': self.client.ace.START( self.reqtype, { 'content_id': self.path_unquoted, 'file_indexes': self.params[0] }) elif self.reqtype == 'torrent': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) elif self.reqtype == 'infohash': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['infohash'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) elif self.reqtype == 'url': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['direct_url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) elif self.reqtype == 'raw': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['data'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict) elif self.reqtype == 'efile': self.client.ace.START(self.reqtype, {'efile_url': self.path_unquoted}) logger.debug("START %s done" % (self.reqtype)) # Getting URL from engine if self.reqtype == 'infohash' or self.reqtype == 'torrent': self.url = self.client.ace.getUrl(AceConfig.videotimeout * 2) else: self.url = self.client.ace.getUrl(AceConfig.videotimeout) # Rewriting host:port for remote Ace Stream Engine p = urlparse.urlsplit(self.url) p = p._replace(netloc=AceConfig.acehost + ':' + str(AceConfig.aceHTTPport)) self.url = urlparse.urlunsplit(p) logger.debug("Successfully get url %s from AceEngine!" % (self.url)) self.errorhappened = False if shouldStart: # If using VLC, add this url to VLC if AceConfig.vlcuse: # Force ffmpeg demuxing if set in config if AceConfig.vlcforceffmpeg: self.vlcprefix = 'http/ffmpeg://' else: self.vlcprefix = '' AceStuff.vlcclient.startBroadcast( self.vlcid, self.vlcprefix + self.url, AceConfig.vlcmux, AceConfig.vlcpreaccess) # Sleep a bit, because sometimes VLC doesn't open port in time gevent.sleep(0.5) self.client.ace.play() self.hanggreenlet = gevent.spawn(self.hangDetector) logger.debug("hangDetector spawned") gevent.sleep() if AceConfig.vlcuse: # Building new VLC url self.url = 'http://' + AceConfig.vlchost + \ ':' + str(AceConfig.vlcoutport) + '/' + self.vlcid logger.debug("VLC output url: " + self.url) # Sending client headers to videostream if self.headers.has_key('range'): del self.headers['range'] logger.debug('Sending client headers to VLC broadcast: %s' % self.headers.dict) self.video = requests.get(self.url, headers=self.headers.dict, stream=True) logger.debug('Got headers from VLC broadcast: %s' % self.video.headers) # Sending videostream headers to client self.send_response(self.video.status_code) if self.video.headers.get('server'): del self.video.headers['server'] if self.video.headers.get('data'): del self.video.headers['data'] if self.video.headers.get('transfer-encoding'): del self.video.headers['transfer-encoding'] if self.video.headers.get('accept-ranges'): del self.video.headers['accept-ranges'] for key in self.video.headers: self.send_header(key, self.video.headers.get(key)) self.end_headers() logger.debug('Sending HTTPAceProxy headers to client: %s' % self.video.headers) # Run proxyReadWrite self.proxyReadWrite() else: if not fmt: fmt = self.reqparams.get( 'fmt')[0] if self.reqparams.has_key('fmt') else None # Start translation self.client.handle(shouldStart, self.url, fmt, self.headers.dict) except (aceclient.AceException, vlcclient.VlcException, requests.exceptions.ConnectionError) as e: logger.error("Exception: " + repr(e)) self.errorhappened = True self.dieWithError() except gevent.GreenletExit: # hangDetector told us about client disconnection logger.debug('greenletExit') pass except Exception: # Unknown exception logger.error(traceback.format_exc()) self.errorhappened = True self.dieWithError() finally: if not self.errorhappened and AceStuff.clientcounter.count( cid) == 1: # If no error happened and we are the only client try: gevent.sleep() #VIDEO_DESTROY_DELAY except: pass try: remaining = AceStuff.clientcounter.delete(cid, self.client) if self.client: self.client.destroy() self.ace = None self.client = None if AceConfig.vlcuse and remaining == 0: try: AceStuff.vlcclient.stopBroadcast(self.vlcid) except: pass logger.debug("END REQUEST") except: logger.error(traceback.format_exc())
def handleRequest(self, headers_only, channelName=None, channelIcon=None, fmt=None): logger = logging.getLogger('HandleRequest') self.requrl = requests.utils.urlparse(self.path) self.reqparams = parse_qs(self.query) self.path = self.requrl.path[:-1] if self.requrl.path.endswith( '/') else self.requrl.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') # If firewall enabled if AceConfig.firewall and not checkFirewall(self.clientip): self.dieWithError( 403, 'Dropping connection from %s due to firewall rules' % self.clientip, logging.ERROR) # 403 Forbidden 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( 400, 'Request seems like valid but no valid video extension was provided', logging.ERROR) return except IndexError: self.dieWithError(400, 'Bad Request', logging.WARNING) return # 400 Bad Request # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: self.dieWithError( 503, "Maximum client connections reached, can't serve this", logging.ERROR) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg") self.send_header("Connection", "Close") self.end_headers() return # Make dict with parameters # [file_indexes, developer_id, affiliate_id, zone_id, stream_id] paramsdict = dict() for i in xrange(3, 8): try: paramsdict.update({ aceclient.acemessages.AceConst.START_PARAMS[i - 3]: int(self.splittedpath[i]) }) except (IndexError, ValueError): paramsdict.update( {aceclient.acemessages.AceConst.START_PARAMS[i - 3]: '0'}) paramsdict[self.reqtype] = requests.utils.unquote( self.splittedpath[2]) #self.path_unquoted #End parameters dict content_id = self.getCID(self.reqtype, paramsdict[self.reqtype]) CID = content_id if content_id else paramsdict[self.reqtype] if not channelName and self.reqtype in ('content_id', 'url', 'infohash'): try: headers = { 'User-Agent': 'Python-urllib/2.7', 'Content-Type': 'application/octet-stream', 'Connection': 'close' } url = 'http://%s:%s/server/api' % (AceConfig.acehost, AceConfig.aceHTTPport) params = { 'method': 'get_media_files', self.reqtype: paramsdict[self.reqtype] } channelName = requests.get(url, headers=headers, params=params, timeout=5).json()['result'][str( paramsdict['file_indexes'])] except: channelName = CID if not channelIcon: channelIcon = 'http://static.acestream.net/sites/acestream/img/ACE-logo.png' # Create client self.client = Client(CID, self, channelName, channelIcon) try: # If there is no existing broadcast we create it if AceStuff.clientcounter.add(CID, self.client) == 1: logger.warning('Create a broadcast "%s"' % self.client.channelName) # Send START commands to AceEngine self.client.ace.START(self.reqtype, paramsdict, AceConfig.streamtype) # Getting URL from engine self.url = self.client.ace.getUrl( AceConfig.videotimeout * 2) if self.reqtype in ('infohash', 'url', 'data') else self.client.ace.getUrl( AceConfig.videotimeout) # Rewriting host:port for remote Ace Stream Engine p = requests.utils.urlparse( self.url)._replace(netloc=AceConfig.acehost + ':' + str(AceConfig.aceHTTPport)) self.url = requests.utils.urlunparse(p) # Start streamreader for broadcast gevent.spawn(self.client.ace.startStreamReader, self.url, CID, AceStuff.clientcounter, self.headers.dict) gevent.sleep() logger.warning('Broadcast "%s" created' % self.client.channelName) except aceclient.AceException as e: self.dieWithError(500, 'AceClient exception: %s' % repr(e)) except Exception as e: self.dieWithError(500, 'Unkonwn exception: %s' % repr(e)) else: if not fmt: fmt = self.reqparams.get( 'fmt')[0] if 'fmt' in self.reqparams else None # streaming to client logger.info('Streaming "%s" to %s started' % (self.client.channelName, self.clientip)) self.client.handle(fmt) logger.info('Streaming "%s" to %s finished' % (self.client.channelName, self.clientip)) finally: if AceStuff.clientcounter.delete(CID, self.client) == 0: logger.warning( 'Broadcast "%s" stoped. Last client disconnected' % self.client.channelName) self.client.destroy() return
def handleRequest(self, headers_only, channelName=None, channelIcon=None, fmt=None): logger = logging.getLogger('HandleRequest') self.requrl = urlparse(self.path) self.reqparams = parse_qs(self.requrl.query) self.path = self.requrl.path[:-1] if self.requrl.path.endswith( '/') else self.requrl.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') # If firewall enabled if AceConfig.firewall and not checkFirewall(self.clientip): self.dieWithError( 403, 'Dropping connection from %s due to firewall rules' % self.clientip, logging.ERROR) # 403 Forbidden return # Check if third parameter exists…/pid/blablablablabla/video.mpg # |_________| # And if it ends with regular video extension try: if not self.path.endswith(self.videoextdefaults): self.dieWithError( 400, 'Request seems like valid but no valid video extension was provided', logging.ERROR) return except IndexError: self.dieWithError(400, 'Bad Request', logging.WARNING) return # 400 Bad Request # Limit concurrent connections if 0 < AceConfig.maxconns <= AceStuff.clientcounter.total: self.dieWithError( 503, "Maximum client connections reached, can't serve this", logging.ERROR) # 503 Service Unavailable return # Pretend to work fine with Fake or HEAD request. if headers_only or AceConfig.isFakeRequest(self.path, self.reqparams, 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/mpeg") self.send_header("Connection", "Close") self.end_headers() self.closeConnection() return # Check is AceStream engine alive before start streaming if self.connection: checkAce() # Make list with parameters # file_indexes developer_id affiliate_id zone_id stream_id self.params = list() for i in xrange(3, 8): try: self.params.append(int(self.splittedpath[i])) except (IndexError, ValueError): self.params.append('0') # End parameters self.url = None CID = self.path_unquoted = requests.utils.unquote(self.splittedpath[2]) if self.reqtype == 'torrent': CID = self.getInfohash(self.reqtype, self.path_unquoted) if not CID: CID = self.getCid(self.reqtype, self.path_unquoted) if not CID: CID = self.path_unquoted self.client = Client(CID, self, channelName, channelIcon) try: # If there is no existing broadcast if AceStuff.clientcounter.add(CID, self.client) == 1: logger.warning('Create a broadcast.....') # Send commands to AceEngine if self.reqtype == 'pid': self.client.ace.START( self.reqtype, { 'content_id': self.path_unquoted, 'file_indexes': self.params[0] }, AceConfig.streamtype) elif self.reqtype == 'torrent': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict, AceConfig.streamtype) elif self.reqtype == 'infohash': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['infohash'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict, AceConfig.streamtype) elif self.reqtype == 'url': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['direct_url'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict, AceConfig.streamtype) elif self.reqtype == 'raw': paramsdict = dict( zip(aceclient.acemessages.AceConst.START_PARAMS, self.params)) paramsdict['data'] = self.path_unquoted self.client.ace.START(self.reqtype, paramsdict, AceConfig.streamtype) elif self.reqtype == 'efile': self.client.ace.START(self.reqtype, {'efile_url': self.path_unquoted}, AceConfig.streamtype) # Getting URL from engine if self.reqtype == 'infohash' or self.reqtype == 'torrent': self.url = self.client.ace.getUrl(AceConfig.videotimeout * 2) else: self.url = self.client.ace.getUrl(AceConfig.videotimeout) # Rewriting host:port for remote Ace Stream Engine p = urlsplit( self.url)._replace(netloc=AceConfig.acehost + ':' + str(AceConfig.aceHTTPport)) self.url = urlunsplit(p) self.client.ace.play() # send EVENT play to AceSnream Engine # Start streamreader for broadcast gevent.spawn(self.client.ace.startStreamReader, self.url, CID, AceStuff.clientcounter, self.headers.dict) gevent.sleep() logger.warning('Broadcast "%s" created' % (self.client.channelName if self.client.channelName != None else CID)) except aceclient.AceException as e: self.dieWithError(500, 'AceClient exception: %s' % repr(e)) except Exception as e: self.dieWithError(500, 'Unkonwn exception: %s' % repr(e)) else: if not fmt: fmt = self.reqparams.get( 'fmt')[0] if 'fmt' in self.reqparams else None # streaming to client logger.info( 'Streaming "%s" to %s started' % (self.client.channelName if self.client.channelName != None else CID, self.clientip)) self.client.handle(fmt) logger.info( 'Streaming "%s" to %s finished' % (self.client.channelName if self.client.channelName != None else CID, self.clientip)) finally: if AceStuff.clientcounter.delete(CID, self.client) == 0: logger.warning( 'Broadcast "%s" destroyed. Last client disconnected' % (self.client.channelName if self.client.channelName != None else CID)) self.client.destroy() self.client = None