diff --git a/mediaserver/HTTPServer.py b/mediaserver/HTTPAndWSServer.py similarity index 67% rename from mediaserver/HTTPServer.py rename to mediaserver/HTTPAndWSServer.py index 92cf680e..9703ddf1 100644 --- a/mediaserver/HTTPServer.py +++ b/mediaserver/HTTPAndWSServer.py @@ -5,17 +5,17 @@ import re import threading import time import traceback -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from BaseHTTPServer import HTTPServer +from HTTPWebSocketsHandler import HTTPWebSocketsHandler from platformcode import config, logger - +from core import jsontools as json class MyHTTPServer(HTTPServer): daemon_threads = True def process_request_thread(self, request, client_address): try: - self.finish_request(request, client_address) self.shutdown_request(request) except: @@ -35,12 +35,15 @@ class MyHTTPServer(HTTPServer): logger.error(traceback.format_exc()) -class Handler(BaseHTTPRequestHandler): +class Handler(HTTPWebSocketsHandler): def log_message(self, format, *args): # sys.stderr.write("%s - - [%s] %s\n" %(self.client_address[0], self.log_date_time_string(), format%args)) pass - def do_GET(self): + def sendMessage(self, message): + self.send_message(message) + + def do_GET_HTTP(self): from platformcode import platformtools from platformcode import controllers # Control de accesos @@ -87,6 +90,40 @@ class Handler(BaseHTTPRequestHandler): del c return + def on_ws_message(self, message): + try: + if message: + json_message = json.load(message) + + if "request" in json_message: + t = threading.Thread(target=run, args=[self.controller, json_message["request"].encode("utf8")], name=self.ID) + t.setDaemon(True) + t.start() + + elif "data" in json_message: + if type(json_message["data"]["result"]) == unicode: + json_message["data"]["result"] = json_message["data"]["result"].encode("utf8") + + self.controller.data = json_message["data"] + + except: + logger.error(traceback.format_exc()) + show_error_message(traceback.format_exc()) + + def on_ws_connected(self): + try: + self.ID = "%032x" % (random.getrandbits(128)) + from platformcode.controllers.html import html + self.controller = html(self, self.ID) + self.server.fnc_info() + except: + logger.error(traceback.format_exc()) + + def on_ws_closed(self): + self.controller.__del__() + del self.controller + self.server.fnc_info() + def address_string(self): # Disable reverse name lookups return self.client_address[:2][0] @@ -95,6 +132,13 @@ class Handler(BaseHTTPRequestHandler): PORT = config.get_setting("server.port") server = MyHTTPServer(('', int(PORT)), Handler) +def run(controller, path): + try: + controller.run(path) + except: + logger.error(traceback.format_exc()) + show_error_message(traceback.format_exc()) + def start(fnc_info): server.fnc_info = fnc_info diff --git a/mediaserver/WebSocket.py b/mediaserver/WebSocket.py deleted file mode 100644 index 083f692d..00000000 --- a/mediaserver/WebSocket.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -# ------------------------------------------------------------ -# HTTPServer -# ------------------------------------------------------------ -import os -import random -import traceback -from threading import Thread - -import WebSocketServer - -from core import jsontools as json -from platformcode import config, platformtools, logger - - -class HandleWebSocket(WebSocketServer.WebSocket): - def handleMessage(self): - try: - if self.data: - json_message = json.load(str(self.data)) - - if "request" in json_message: - t = Thread(target=run, args=[self.controller, json_message["request"].encode("utf8")], name=self.ID) - t.setDaemon(True) - t.start() - - elif "data" in json_message: - if type(json_message["data"]["result"]) == unicode: - json_message["data"]["result"] = json_message["data"]["result"].encode("utf8") - - self.controller.data = json_message["data"] - - except: - logger.error(traceback.format_exc()) - show_error_message(traceback.format_exc()) - - def handleConnected(self): - try: - self.ID = "%032x" % (random.getrandbits(128)) - from platformcode.controllers.html import html - self.controller = html(self, self.ID) - self.server.fnc_info() - except: - logger.error(traceback.format_exc()) - self.close() - - def handleClose(self): - self.controller.__del__() - del self.controller - self.server.fnc_info() - - -port = config.get_setting("websocket.port") -server = WebSocketServer.SimpleWebSocketServer("", int(port), HandleWebSocket) - - -def start(fnc_info): - server.fnc_info = fnc_info - Thread(target=server.serveforever).start() - - -def stop(): - server.close() - - -def run(controller, path): - try: - controller.run(path) - except: - logger.error(traceback.format_exc()) - show_error_message(traceback.format_exc()) - - -def show_error_message(err_info): - from core import scrapertools - patron = 'File "' + os.path.join(config.get_runtime_path(), "channels", "").replace("\\", "\\\\") + '([^.]+)\.py"' - canal = scrapertools.find_single_match(err_info, patron) - if canal: - platformtools.dialog_ok( - "Se ha producido un error en el canal " + canal, - "Esto puede ser devido a varias razones: \n \ - - El servidor no está disponible, o no esta respondiendo.\n \ - - Cambios en el diseño de la web.\n \ - - Etc...\n \ - Comprueba el log para ver mas detalles del error.") - else: - platformtools.dialog_ok( - "Se ha producido un error en Alfa", - "Comprueba el log para ver mas detalles del error.") diff --git a/mediaserver/alfa.py b/mediaserver/alfa.py index f90710ee..e53fe499 100644 --- a/mediaserver/alfa.py +++ b/mediaserver/alfa.py @@ -14,12 +14,11 @@ from platformcode import config sys.path.append(os.path.join(config.get_runtime_path(), 'lib')) from platformcode import platformtools, logger -import HTTPServer -import WebSocket +import HTTPAndWSServer http_port = config.get_setting("server.port") -websocket_port = config.get_setting("websocket.port") myip = config.get_local_ip() +version = config.get_addon_version() def thread_name_wrap(func): @@ -43,9 +42,8 @@ if sys.version_info < (2, 7, 11): def show_info(): os.system('cls' if os.name == 'nt' else 'clear') print ("--------------------------------------------------------------------") - print ("Alfa Iniciado") + print ("Alfa %s Iniciado" %version) print ("La URL para acceder es http://%s:%s" % (myip, http_port)) - print ("WebSocket Server iniciado en ws://%s:%s" % (myip, websocket_port)) print ("--------------------------------------------------------------------") print ("Runtime Path : " + config.get_runtime_path()) print ("Data Path : " + config.get_data_path()) @@ -67,14 +65,12 @@ def start(): logger.info("server init...") config.verify_directories_created() try: - HTTPServer.start(show_info) - WebSocket.start(show_info) + HTTPAndWSServer.start(show_info) # Da por levantado el servicio logger.info("--------------------------------------------------------------------") - logger.info("Alfa Iniciado") + logger.info("Alfa %s Iniciado" %version) logger.info("La URL para acceder es http://%s:%s" % (myip, http_port)) - logger.info("WebSocket Server iniciado en ws://%s:%s" % (myip, websocket_port)) logger.info("--------------------------------------------------------------------") logger.info("Runtime Path : " + config.get_runtime_path()) logger.info("Data Path : " + config.get_data_path()) @@ -91,9 +87,7 @@ def start(): except KeyboardInterrupt: print 'Deteniendo el servidor HTTP...' - HTTPServer.stop() - print 'Deteniendo el servidor WebSocket...' - WebSocket.stop() + HTTPAndWSServer.stop() print 'Alfa Detenido' flag = False diff --git a/mediaserver/lib/HTTPWebSocketsHandler.py b/mediaserver/lib/HTTPWebSocketsHandler.py new file mode 100644 index 00000000..3e89b503 --- /dev/null +++ b/mediaserver/lib/HTTPWebSocketsHandler.py @@ -0,0 +1,229 @@ +''' +The MIT License (MIT) + +Copyright (C) 2014, 2015 Seven Watt + + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +''' + +# HTTPWebSocketHandler from SevenW: https://github.com/SevenW/httpwebsockethandler + +from SimpleHTTPServer import SimpleHTTPRequestHandler +import struct +from base64 import b64encode +from hashlib import sha1 +from mimetools import Message +from StringIO import StringIO +import errno, socket #for socket exceptions +import threading + +class WebSocketError(Exception): + pass + +class HTTPWebSocketsHandler(SimpleHTTPRequestHandler): + _ws_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' + _opcode_continu = 0x0 + _opcode_text = 0x1 + _opcode_binary = 0x2 + _opcode_close = 0x8 + _opcode_ping = 0x9 + _opcode_pong = 0xa + + mutex = threading.Lock() + + def on_ws_message(self, message): + """Override this handler to process incoming websocket messages.""" + pass + + def on_ws_connected(self): + """Override this handler.""" + pass + + def on_ws_closed(self): + """Override this handler.""" + pass + + def do_GET_HTTP(self): + """Override this handler.""" + SimpleHTTPRequestHandler.do_GET(self) + pass + + def send_message(self, message): + self._send_message(self._opcode_text, message) + + def setup(self): + SimpleHTTPRequestHandler.setup(self) + self.connected = False + + # def finish(self): + # #needed when wfile is used, or when self.close_connection is not used + # # + # #catch errors in SimpleHTTPRequestHandler.finish() after socket disappeared + # #due to loss of network connection + # try: + # SimpleHTTPRequestHandler.finish(self) + # except (socket.error, TypeError) as err: + # self.log_message("finish(): Exception: in SimpleHTTPRequestHandler.finish(): %s" % str(err.args)) + + # def handle(self): + # #needed when wfile is used, or when self.close_connection is not used + # # + # #catch errors in SimpleHTTPRequestHandler.handle() after socket disappeared + # #due to loss of network connection + # try: + # SimpleHTTPRequestHandler.handle(self) + # except (socket.error, TypeError) as err: + # self.log_message("handle(): Exception: in SimpleHTTPRequestHandler.handle(): %s" % str(err.args)) + + def checkAuthentication(self): + auth = self.headers.get('Authorization') + if auth != "Basic %s" % self.server.auth: + self.send_response(401) + self.send_header("WWW-Authenticate", 'Basic realm="Plugwise"') + self.end_headers(); + return False + return True + + def do_GET(self): + # if self.server.auth and not self.checkAuthentication(): + # return + if self.headers.get("Upgrade", None) == "websocket": + self._handshake() + #This handler is in websocket mode now. + #do_GET only returns after client close or socket error. + self._read_messages() + else: + self.do_GET_HTTP() + + def _read_messages(self): + while self.connected == True: + try: + self._read_next_message() + except (socket.error, WebSocketError), e: + #websocket content error, time-out or disconnect. + self.log_message("RCV: Close connection: Socket Error %s" % str(e.args)) + self._ws_close() + except Exception as err: + #unexpected error in websocket connection. + self.log_error("RCV: Exception: in _read_messages: %s" % str(err.args)) + self._ws_close() + + def _read_next_message(self): + #self.rfile.read(n) is blocking. + #it returns however immediately when the socket is closed. + try: + self.opcode = ord(self.rfile.read(1)) & 0x0F + length = ord(self.rfile.read(1)) & 0x7F + if length == 126: + length = struct.unpack(">H", self.rfile.read(2))[0] + elif length == 127: + length = struct.unpack(">Q", self.rfile.read(8))[0] + masks = [ord(byte) for byte in self.rfile.read(4)] + decoded = "" + for char in self.rfile.read(length): + decoded += chr(ord(char) ^ masks[len(decoded) % 4]) + self._on_message(decoded) + except (struct.error, TypeError) as e: + #catch exceptions from ord() and struct.unpack() + if self.connected: + raise WebSocketError("Websocket read aborted while listening") + else: + #the socket was closed while waiting for input + self.log_error("RCV: _read_next_message aborted after closed connection") + pass + + def _send_message(self, opcode, message): + try: + #use of self.wfile.write gives socket exception after socket is closed. Avoid. + self.request.send(chr(0x80 + opcode)) + length = len(message) + if length <= 125: + self.request.send(chr(length)) + elif length >= 126 and length <= 65535: + self.request.send(chr(126)) + self.request.send(struct.pack(">H", length)) + else: + self.request.send(chr(127)) + self.request.send(struct.pack(">Q", length)) + if length > 0: + self.request.send(message) + except socket.error, e: + #websocket content error, time-out or disconnect. + self.log_message("SND: Close connection: Socket Error %s" % str(e.args)) + self._ws_close() + except Exception as err: + #unexpected error in websocket connection. + self.log_error("SND: Exception: in _send_message: %s" % str(err.args)) + self._ws_close() + + def _handshake(self): + headers=self.headers + if headers.get("Upgrade", None) != "websocket": + return + key = headers['Sec-WebSocket-Key'] + digest = b64encode(sha1(key + self._ws_GUID).hexdigest().decode('hex')) + self.send_response(101, 'Switching Protocols') + self.send_header('Upgrade', 'websocket') + self.send_header('Connection', 'Upgrade') + self.send_header('Sec-WebSocket-Accept', str(digest)) + self.end_headers() + self.connected = True + #self.close_connection = 0 + self.on_ws_connected() + + def _ws_close(self): + #avoid closing a single socket two time for send and receive. + self.mutex.acquire() + try: + if self.connected: + self.connected = False + #Terminate BaseHTTPRequestHandler.handle() loop: + self.close_connection = 1 + #send close and ignore exceptions. An error may already have occurred. + try: + self._send_close() + except: + pass + self.on_ws_closed() + else: + self.log_message("_ws_close websocket in closed state. Ignore.") + pass + finally: + self.mutex.release() + + def _on_message(self, message): + #self.log_message("_on_message: opcode: %02X msg: %s" % (self.opcode, message)) + + # close + if self.opcode == self._opcode_close: + self.connected = False + #Terminate BaseHTTPRequestHandler.handle() loop: + self.close_connection = 1 + try: + self._send_close() + except: + pass + self.on_ws_closed() + # ping + elif self.opcode == self._opcode_ping: + _send_message(self._opcode_pong, message) + # pong + elif self.opcode == self._opcode_pong: + pass + # data + elif (self.opcode == self._opcode_continu or + self.opcode == self._opcode_text or + self.opcode == self._opcode_binary): + self.on_ws_message(message) + + def _send_close(self): + #Dedicated _send_close allows for catch all exception handling + msg = bytearray() + msg.append(0x80 + self._opcode_close) + msg.append(0x00) + self.request.send(msg) diff --git a/mediaserver/lib/WebSocketServer.py b/mediaserver/lib/WebSocketServer.py deleted file mode 100644 index ff20570c..00000000 --- a/mediaserver/lib/WebSocketServer.py +++ /dev/null @@ -1,648 +0,0 @@ -''' -The MIT License (MIT) - -Copyright (c) 2013 Dave P. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -''' - -import SocketServer -import hashlib -import base64 -import socket -import struct -import ssl -import time -import sys -import errno -import logging -from BaseHTTPServer import BaseHTTPRequestHandler -from StringIO import StringIO -from select import select - - -class HTTPRequest(BaseHTTPRequestHandler): - def __init__(self, request_text): - self.rfile = StringIO(request_text) - self.raw_requestline = self.rfile.readline() - self.error_code = self.error_message = None - self.parse_request() - - -class WebSocket(object): - - handshakeStr = ( - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %(acceptstr)s\r\n\r\n" - ) - - hixiehandshakedStr = ( - "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: %(origin)s\r\n" - "Sec-WebSocket-Location: %(type)s://%(host)s%(location)s\r\n\r\n" - ) - - hixie75handshakedStr = ( - "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "WebSocket-Origin: %(origin)s\r\n" - "WebSocket-Location: %(type)s://%(host)s%(location)s\r\n\r\n" - ) - GUIDStr = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' - - STREAM = 0x0 - TEXT = 0x1 - BINARY = 0x2 - CLOSE = 0x8 - PING = 0x9 - PONG = 0xA - - HEADERB1 = 1 - HEADERB2 = 3 - LENGTHSHORT = 4 - LENGTHLONG = 5 - MASK = 6 - PAYLOAD = 7 - - def __init__(self, server, sock, address): - self.server = server - self.client = sock - self.address = address - - self.handshaked = False - self.headerbuffer = '' - self.readdraftkey = False - self.draftkey = '' - self.headertoread = 2048 - self.hixie76 = False - - self.fin = 0 - self.data = None - self.opcode = 0 - self.hasmask = 0 - self.maskarray = None - self.length = 0 - self.lengtharray = None - self.index = 0 - self.request = None - self.usingssl = False - - self.state = self.HEADERB1 - - # restrict the size of header and payload for security reasons - self.maxheader = 65536 - self.maxpayload = 4194304 - - def close(self): - self.client.close() - self.state = self.HEADERB1 - self.hasmask = False - self.handshaked = False - self.readdraftkey = False - self.hixie76 = False - self.headertoread = 2048 - self.headerbuffer = '' - self.data = '' - - - def handleMessage(self): - pass - - def handleConnected(self): - pass - - def handleClose(self): - pass - - def handlePacket(self): - # close - if self.opcode == self.CLOSE: - self.sendClose() - raise Exception("received client close") - # ping - elif self.opcode == self.PING: - pass - - # pong - elif self.opcode == self.PONG: - pass - - # data - elif self.opcode == self.STREAM or self.opcode == self.TEXT or self.opcode == self.BINARY: - self.handleMessage() - - - def handleData(self): - - # do the HTTP header and handshake - if self.handshaked is False: - - data = self.client.recv(self.headertoread) - - if data: - # accumulate - self.headerbuffer += data - - if len(self.headerbuffer) >= self.maxheader: - raise Exception('header exceeded allowable size') - - # we need to read the entire 8 bytes of after the HTTP header, ensure we do - if self.readdraftkey is True: - self.draftkey += self.headerbuffer - read = self.headertoread - len(self.headerbuffer) - - if read != 0: - self.headertoread = read - else: - # complete hixie76 handshake - self.handshake_hixie76() - - # indicates end of HTTP header - elif '\r\n\r\n' in self.headerbuffer: - self.request = HTTPRequest(self.headerbuffer) - # hixie handshake - if self.request.headers.has_key('Sec-WebSocket-Key1'.lower()) and self.request.headers.has_key('Sec-WebSocket-Key2'.lower()): - # check if we have the key in our buffer - index = self.headerbuffer.find('\r\n\r\n') + 4 - # determine how much of the 8 byte key we have - read = len(self.headerbuffer) - index - # do we have all the 8 bytes we need? - if read < 8: - self.headertoread = 8 - read - self.readdraftkey = True - if read > 0: - self.draftkey += self.headerbuffer[index:index+read] - - else: - # get the key - self.draftkey += self.headerbuffer[index:index+8] - # complete hixie handshake - self.handshake_hixie76() - - # handshake rfc 6455 - elif self.request.headers.has_key('Sec-WebSocket-Key'.lower()): - key = self.request.headers['Sec-WebSocket-Key'.lower()] - hStr = self.handshakeStr % { 'acceptstr' : base64.b64encode(hashlib.sha1(key + self.GUIDStr).digest()) } - self.sendBuffer(hStr) - self.handshaked = True - self.headerbuffer = '' - - try: - self.handleConnected() - except: - pass - # hixie 75 - else: - self.handshake_hixie75() - #raise Exception('Sec-WebSocket-Key does not exist') - - # remote connection has been closed - else: - raise Exception("remote socket closed") - - # else do normal data - else: - data = self.client.recv(2048) - if data: - for val in data: - if self.hixie76 is False: - self.parseMessage(ord(val)) - else: - self.parseMessage_hixie76(ord(val)) - else: - raise Exception("remote socket closed") - - - def handshake_hixie75(self): - typestr = 'ws' - if self.usingssl is True: - typestr = 'wss' - - response = self.hixie75handshakedStr % { 'type' : typestr, 'origin' : self.request.headers['Origin'.lower()], 'host' : self.request.headers['Host'.lower()], 'location' : self.request.path } - - self.sendBuffer(response) - - - self.handshaked = True - self.headerbuffer = '' - self.hixie76 = True - - try: - self.handleConnected() - except: - pass - - def handshake_hixie76(self): - - k1 = self.request.headers['Sec-WebSocket-Key1'.lower()] - k2 = self.request.headers['Sec-WebSocket-Key2'.lower()] - - spaces1 = k1.count(" ") - spaces2 = k2.count(" ") - num1 = int("".join([c for c in k1 if c.isdigit()])) / spaces1 - num2 = int("".join([c for c in k2 if c.isdigit()])) / spaces2 - - key = '' - key += struct.pack('>I', num1) - key += struct.pack('>I', num2) - key += self.draftkey - - typestr = 'ws' - if self.usingssl is True: - typestr = 'wss' - - response = self.hixiehandshakedStr % { 'type' : typestr, 'origin' : self.request.headers['Origin'.lower()], 'host' : self.request.headers['Host'.lower()], 'location' : self.request.path } - - self.sendBuffer(response) - self.sendBuffer(hashlib.md5(key).digest()) - - self.handshaked = True - self.hixie76 = True - self.headerbuffer = '' - - try: - self.handleConnected() - except: - pass - - - def sendClose(self): - - msg = bytearray() - if self.hixie76 is False: - msg.append(0x88) - msg.append(0x00) - self.sendBuffer(msg) - else: - pass - - def sendBuffer(self, buff): - size = len(buff) - tosend = size - index = 0 - - while tosend > 0: - try: - # i should be able to send a bytearray - sent = self.client.send(str(buff[index:size])) - if sent == 0: - raise RuntimeError("socket connection broken") - - index += sent - tosend -= sent - - except socket.error as e: - # if we have full buffers then wait for them to drain and try again - if e.errno == errno.EAGAIN: - time.sleep(0.001) - else: - raise e - - - #if s is a string then websocket TEXT is sent else BINARY - def sendMessage(self, s): - - if self.hixie76 is False: - - header = bytearray() - isString = isinstance(s, str) - - if isString is True: - header.append(0x81) - else: - header.append(0x82) - - b2 = 0 - length = len(s) - - if length <= 125: - b2 |= length - header.append(b2) - - elif length >= 126 and length <= 65535: - b2 |= 126 - header.append(b2) - header.extend(struct.pack("!H", length)) - - else: - b2 |= 127 - header.append(b2) - header.extend(struct.pack("!Q", length)) - - if length > 0: - self.sendBuffer(header + s) - else: - self.sendBuffer(header) - header = None - - else: - msg = bytearray() - msg.append(0) - if len(s) > 0: - msg.extend(str(s).encode("UTF8")) - msg.append(0xFF) - - self.sendBuffer(msg) - msg = None - - - def parseMessage_hixie76(self, byte): - - if self.state == self.HEADERB1: - if byte == 0: - self.state = self.PAYLOAD - self.data = bytearray() - - elif self.state == self.PAYLOAD: - if byte == 0xFF: - self.opcode = 1 - self.length = len(self.data) - try: - self.handlePacket() - finally: - self.data = None - self.state = self.HEADERB1 - else : - self.data.append(byte) - # if length exceeds allowable size then we except and remove the connection - if len(self.data) >= self.maxpayload: - raise Exception('payload exceeded allowable size') - - - def parseMessage(self, byte): - # read in the header - if self.state == self.HEADERB1: - # fin - self.fin = (byte & 0x80) - # get opcode - self.opcode = (byte & 0x0F) - - self.state = self.HEADERB2 - - elif self.state == self.HEADERB2: - mask = byte & 0x80 - length = byte & 0x7F - - if mask == 128: - self.hasmask = True - else: - self.hasmask = False - - if length <= 125: - self.length = length - - # if we have a mask we must read it - if self.hasmask is True: - self.maskarray = bytearray() - self.state = self.MASK - else: - # if there is no mask and no payload we are done - if self.length <= 0: - try: - self.handlePacket() - finally: - self.state = self.HEADERB1 - self.data = None - - # we have no mask and some payload - else: - self.index = 0 - self.data = bytearray() - self.state = self.PAYLOAD - - elif length == 126: - self.lengtharray = bytearray() - self.state = self.LENGTHSHORT - - elif length == 127: - self.lengtharray = bytearray() - self.state = self.LENGTHLONG - - - elif self.state == self.LENGTHSHORT: - self.lengtharray.append(byte) - - if len(self.lengtharray) > 2: - raise Exception('short length exceeded allowable size') - - if len(self.lengtharray) == 2: - self.length = struct.unpack_from('!H', str(self.lengtharray))[0] - - if self.hasmask is True: - self.maskarray = bytearray() - self.state = self.MASK - else: - # if there is no mask and no payload we are done - if self.length <= 0: - try: - self.handlePacket() - finally: - self.state = self.HEADERB1 - self.data = None - - # we have no mask and some payload - else: - self.index = 0 - self.data = bytearray() - self.state = self.PAYLOAD - - elif self.state == self.LENGTHLONG: - - self.lengtharray.append(byte) - - if len(self.lengtharray) > 8: - raise Exception('long length exceeded allowable size') - - if len(self.lengtharray) == 8: - self.length = struct.unpack_from('!Q', str(self.lengtharray))[0] - - if self.hasmask is True: - self.maskarray = bytearray() - self.state = self.MASK - else: - # if there is no mask and no payload we are done - if self.length <= 0: - try: - self.handlePacket() - finally: - self.state = self.HEADERB1 - self.data = None - - # we have no mask and some payload - else: - self.index = 0 - self.data = bytearray() - self.state = self.PAYLOAD - - # MASK STATE - elif self.state == self.MASK: - self.maskarray.append(byte) - - if len(self.maskarray) > 4: - raise Exception('mask exceeded allowable size') - - if len(self.maskarray) == 4: - # if there is no mask and no payload we are done - if self.length <= 0: - try: - self.handlePacket() - finally: - self.state = self.HEADERB1 - self.data = None - - # we have no mask and some payload - else: - self.index = 0 - self.data = bytearray() - self.state = self.PAYLOAD - - # PAYLOAD STATE - elif self.state == self.PAYLOAD: - if self.hasmask is True: - self.data.append( byte ^ self.maskarray[self.index % 4] ) - else: - self.data.append( byte ) - - # if length exceeds allowable size then we except and remove the connection - if len(self.data) >= self.maxpayload: - raise Exception('payload exceeded allowable size') - - # check if we have processed length bytes; if so we are done - if (self.index+1) == self.length: - try: - self.handlePacket() - finally: - self.state = self.HEADERB1 - self.data = None - else: - self.index += 1 - - -class SimpleWebSocketServer(object): - def __init__(self, host, port, websocketclass): - self.websocketclass = websocketclass - self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.serversocket.bind((host, port)) - self.serversocket.listen(5) - self.connections = {} - self.listeners = [self.serversocket] - - - def decorateSocket(self, sock): - return sock - - def constructWebSocket(self, sock, address): - return self.websocketclass(self, sock, address) - - def close(self): - self.serversocket.close() - - for conn in self.connections.itervalues(): - try: - conn.handleClose() - except: - pass - - conn.close() - - - def serveforever(self): - while True: - try: - rList, wList, xList = select(self.listeners, [], self.listeners, 1) - - for ready in rList: - if ready == self.serversocket: - try: - sock, address = self.serversocket.accept() - newsock = self.decorateSocket(sock) - newsock.setblocking(0) - fileno = newsock.fileno() - self.listeners.append(fileno) - self.connections[fileno] = self.constructWebSocket(newsock, address) - - except Exception as n: - - #logging.debug(str(address) + ' ' + str(n)) - - if sock is not None: - sock.close() - else: - client = self.connections[ready] - - try: - client.handleData() - - except Exception as n: - - #logging.debug(str(client.address) + ' ' + str(n)) - - try: - client.handleClose() - except: - pass - - client.close() - - del self.connections[ready] - self.listeners.remove(ready) - - for failed in xList: - if failed == self.serversocket: - self.close() - raise Exception("server socket failed") - else: - client = self.connections[failed] - - try: - client.handleClose() - except: - pass - - client.close() - - del self.connections[failed] - self.listeners.remove(failed) - except: - break - -class SimpleSSLWebSocketServer(SimpleWebSocketServer): - - def __init__(self, host, port, websocketclass, certfile, keyfile, version = ssl.PROTOCOL_TLSv1): - - SimpleWebSocketServer.__init__(self, host, port, websocketclass) - - self.cerfile = certfile - self.keyfile = keyfile - self.version = version - - def close(self): - super(SimpleSSLWebSocketServer, self).close() - - def decorateSocket(self, sock): - sslsock = ssl.wrap_socket(sock, - server_side=True, - certfile=self.cerfile, - keyfile=self.keyfile, - ssl_version=self.version) - return sslsock - - def constructWebSocket(self, sock, address): - ws = self.websocketclass(self, sock, address) - ws.usingssl = True - return ws - - def serveforever(self): - super(SimpleSSLWebSocketServer, self).serveforever() - - diff --git a/mediaserver/platformcode/config.py b/mediaserver/platformcode/config.py index 12b0c80b..e8b82980 100644 --- a/mediaserver/platformcode/config.py +++ b/mediaserver/platformcode/config.py @@ -14,6 +14,27 @@ settings_dic = {} adult_setting = {} +def get_addon_version(linea_inicio=0, total_lineas=2): + ''' + Devuelve el número de de versión del addon, obtenido desde el archivo addon.xml + ''' + path = os.path.join(get_runtime_path(), "addon.xml") + f = open(path, "rb") + data = [] + for x, line in enumerate(f): + if x < linea_inicio: continue + if len(data) == total_lineas: break + data.append(line) + f.close() + data1 = "".join(data) + # + aux = re.findall(' 0: + version = aux[0] + return version + + def get_platform(full_version=False): # full_version solo es util en xbmc/kodi ret = { @@ -385,10 +406,8 @@ menufilepath = os.path.join(get_runtime_path(), "resources", "settings.xml") configfilepath = os.path.join(get_data_path(), "settings.xml") if not os.path.exists(get_data_path()): os.mkdir(get_data_path()) -# Literales -TRANSLATION_FILE_PATH = os.path.join(get_runtime_path(), "resources", "language", "Spanish", "strings.po") load_settings() - +TRANSLATION_FILE_PATH = os.path.join(get_runtime_path(), "resources", "language", settings_dic["mediaserver_language"], "strings.po") # modo adulto: # sistema actual 0: Nunca, 1:Siempre, 2:Solo hasta que se reinicie sesión diff --git a/mediaserver/platformcode/controllers/fileserver.py b/mediaserver/platformcode/controllers/fileserver.py index 11ac6df1..41b7dbb0 100644 --- a/mediaserver/platformcode/controllers/fileserver.py +++ b/mediaserver/platformcode/controllers/fileserver.py @@ -20,7 +20,6 @@ class fileserver(Controller): self.handler.send_header('Content-type', 'text/html') self.handler.end_headers() respuesta = f.read() - respuesta = respuesta.replace("{$WSPORT}", str(config.get_setting("websocket.port"))) self.handler.wfile.write(respuesta) f.close() diff --git a/mediaserver/platformcode/controllers/html.py b/mediaserver/platformcode/controllers/html.py index 573171a1..83a6b8b1 100644 --- a/mediaserver/platformcode/controllers/html.py +++ b/mediaserver/platformcode/controllers/html.py @@ -15,14 +15,9 @@ from platformcode import config from core.item import Item from core.tmdb import Tmdb from platformcode import launcher, logger -from core import filetools -# -data = filetools.read(filetools.join(config.get_runtime_path(), "addon.xml")) -aux = re.findall(' 0: - version = aux[0] +## Obtiene la versión del addon +version = config.get_addon_version() class html(Controller): pattern = re.compile("##") @@ -33,7 +28,10 @@ class html(Controller): self.platformtools = platform(self) self.data = {} if self.handler: - self.client_ip = handler.client.getpeername()[0] + if hasattr(handler, "client"): + self.client_ip = handler.client.getpeername()[0] + else: + self.client_ip = handler.client_address[0] self.send_message({"action": "connect", "data": {"version": "Alfa %s" % version, "date": "--/--/----"}}) @@ -110,7 +108,7 @@ class platform(Platformtools): thumbnail=channelselector.get_thumb("back.png", "banner_"))) else: itemlist.insert(0, Item(title="Atrás", action="go_back", - thumbnail=channelselector.get_thumb("back.png"))) + thumbnail=channelselector.get_thumb("back.png", "banner_"))) JsonData = {} JsonData["action"] = "EndItems" @@ -124,17 +122,9 @@ class platform(Platformtools): # Recorremos el itemlist for item in itemlist: - if not item.thumbnail and item.action == "search": item.thumbnail = channelselector.get_thumb("search.png") - if not item.thumbnail and item.folder == True: item.thumbnail = channelselector.get_thumb("folder.png", "banner") - if not item.thumbnail and item.folder == False: item.thumbnail = channelselector.get_thumb("nofolder.png") - if "http://media.xxxxx/" in item.thumbnail and not item.thumbnail.startswith( - "http://media.xxxxxxxx/thumb_"): - - if parent_item.viewmode in ["banner", "channel"]: - item.thumbnail = channelselector.get_thumbnail_path("banner") + os.path.basename(item.thumbnail) - else: - item.thumbnail = channelselector.get_thumbnail_path() + os.path.basename(item.thumbnail) - + if not item.thumbnail and item.action == "search": item.thumbnail = channelselector.get_thumb("search.png", "banner_") + #if not item.thumbnail and item.folder == True: item.thumbnail = channelselector.get_thumb("folder.png", "banner_") + if not item.thumbnail and item.folder == False: item.thumbnail = channelselector.get_thumb("nofolder.png", "banner_") # Estas imagenes no estan en banner, asi que si queremos banner, para que no se vean mal las quitamos elif parent_item.viewmode in ["banner", "channel"] and item.thumbnail.startswith( "http://media.xxxxx/thumb_"): diff --git a/mediaserver/platformcode/template/js/ui.js b/mediaserver/platformcode/template/js/ui.js index 65fa15a7..106f574c 100644 --- a/mediaserver/platformcode/template/js/ui.js +++ b/mediaserver/platformcode/template/js/ui.js @@ -94,7 +94,7 @@ function focus_element(element) { function image_error(thumbnail) { var src = thumbnail.src; if (thumbnail.src.indexOf(domain) == 0) { - thumbnail.src = "https://github.com/alfa-addon/addon/raw/master/plugin.video.alfa/resources/media/general/default/thumb_folder.png"; + thumbnail.src = "https://github.com/alfa-addon/addon/raw/master/plugin.video.alfa/resources/media/themes/default/thumb_folder.png" } else { thumbnail.src = domain + "/proxy/" + encodeURIComponent(btoa(thumbnail.src)); diff --git a/mediaserver/platformcode/template/page.html b/mediaserver/platformcode/template/page.html index 50fe256a..72c53cee 100644 --- a/mediaserver/platformcode/template/page.html +++ b/mediaserver/platformcode/template/page.html @@ -30,7 +30,7 @@ diff --git a/mediaserver/resources/settings.xml b/mediaserver/resources/settings.xml index 5fe48645..8554ff7e 100644 --- a/mediaserver/resources/settings.xml +++ b/mediaserver/resources/settings.xml @@ -3,11 +3,11 @@ - - + + diff --git a/plugin.video.alfa/addon.xml b/plugin.video.alfa/addon.xml index f9207650..4aaa61e3 100755 --- a/plugin.video.alfa/addon.xml +++ b/plugin.video.alfa/addon.xml @@ -1,5 +1,5 @@ - + @@ -22,14 +22,14 @@ » grantorrent » descargas2020 » torrentlocura » torrentrapid » tumejortorrent » tvsinpagar - » mispelisyseries » playview - » clipwatching » cloudvideo - » filevideo » upvid - » vidzella » vivo - » watchvideo » vshare + » mispelisyseries » mejortorrent + » allcalidad » poseidonhd + » tvvip » animeflv + » areadocumental » cuevana3 + » newpct1 » peliculasdk + » animeyt » pelispedia ¤ arreglos internos y actualizado la versión mediaserver - - ¤ Agradecimientos a @alaquepasa y @Pixo506 por colaborar con ésta versión. + ¤ Agradecimientos al equipo ISOD y @alaquepasa por colaborar con ésta versión. Navega con Kodi por páginas web para ver sus videos de manera fácil. diff --git a/plugin.video.alfa/channels/allcalidad.py b/plugin.video.alfa/channels/allcalidad.py index 8c607370..7a84ae36 100755 --- a/plugin.video.alfa/channels/allcalidad.py +++ b/plugin.video.alfa/channels/allcalidad.py @@ -147,10 +147,12 @@ def findvideos(item): match = scrapertools.find_multiple_matches(bloque, '(?is)(?:iframe|script) .*?src="([^"]+)') for url in match: titulo = "Ver en: %s" + text_color = "white" if "goo.gl" in url: url = httptools.downloadpage(url, follow_redirects=False, only_headers=True).headers.get("location", "") if "youtube" in url: - titulo = "[COLOR = yellow]Ver trailer: %s[/COLOR]" + titulo = "Ver trailer: %s" + text_color = "yellow" if "ad.js" in url or "script" in url or "jstags.js" in url: continue elif "vimeo" in url: @@ -158,6 +160,7 @@ def findvideos(item): itemlist.append( item.clone(channel = item.channel, action = "play", + text_color = text_color, title = titulo, url = url )) diff --git a/plugin.video.alfa/channels/animeflv.py b/plugin.video.alfa/channels/animeflv.py index 84b4fd07..14787bcc 100644 --- a/plugin.video.alfa/channels/animeflv.py +++ b/plugin.video.alfa/channels/animeflv.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- import re -import time import urlparse -import urllib from channels import renumbertools from core import httptools @@ -18,13 +16,10 @@ HOST = "https://animeflv.net/" def mainlist(item): logger.info() - itemlist = list() - itemlist.append(Item(channel=item.channel, action="novedades_episodios", title="Últimos episodios", url=HOST)) itemlist.append(Item(channel=item.channel, action="novedades_anime", title="Últimos animes", url=HOST)) itemlist.append(Item(channel=item.channel, action="listado", title="Animes", url=HOST + "browse?order=title")) - itemlist.append(Item(channel=item.channel, title="Buscar por:")) itemlist.append(Item(channel=item.channel, action="search", title=" Título")) itemlist.append(Item(channel=item.channel, action="search_section", title=" Género", url=HOST + "browse", @@ -35,9 +30,7 @@ def mainlist(item): extra="year")) itemlist.append(Item(channel=item.channel, action="search_section", title=" Estado", url=HOST + "browse", extra="status")) - itemlist = renumbertools.show_option(item.channel, itemlist) - return itemlist @@ -48,36 +41,29 @@ def search(item, texto): texto = texto.replace(" ", "+") post = "value=%s" % texto data = httptools.downloadpage(item.url, post=post).data - try: dict_data = jsontools.load(data) - for e in dict_data: if e["id"] != e["last_id"]: _id = e["last_id"] else: _id = e["id"] - url = "%sanime/%s/%s" % (HOST, _id, e["slug"]) title = e["title"] thumbnail = "%suploads/animes/covers/%s.jpg" % (HOST, e["id"]) new_item = item.clone(action="episodios", title=title, url=url, thumbnail=thumbnail) - if e["type"] != "movie": new_item.show = title new_item.context = renumbertools.context(item) else: new_item.contentType = "movie" new_item.contentTitle = title - itemlist.append(new_item) - except: import sys for line in sys.exc_info(): logger.error("%s" % line) return [] - return itemlist @@ -88,39 +74,30 @@ def search_section(item): data = re.sub(r"\n|\r|\t|\s{2}|-\s", "", data) patron = 'id="%s_select"[^>]+>(.*?)' % item.extra data = scrapertools.find_single_match(data, patron) - matches = re.compile('', re.DOTALL).findall(data) - for _id, title in matches: url = "%s?%s=%s&order=title" % (item.url, item.extra, _id) itemlist.append(Item(channel=item.channel, action="listado", title=title, url=url, context=renumbertools.context(item))) - return itemlist def newest(categoria): itemlist = [] - if categoria == 'anime': itemlist = novedades_episodios(Item(url=HOST)) - return itemlist def novedades_episodios(item): logger.info() - data = httptools.downloadpage(item.url).data data = re.sub(r"\n|\r|\t|\s{2}|-\s", "", data) data = scrapertools.find_single_match(data, '

Últimos episodios

.+?