From da68d2957b7f6e7097a54eafb09fa8c9c38ffc4a Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 14:36:04 +0100 Subject: [PATCH 1/6] Delete NodeJS server --- server/package.json | 25 ----- server/server.js | 253 -------------------------------------------- 2 files changed, 278 deletions(-) delete mode 100644 server/package.json delete mode 100644 server/server.js diff --git a/server/package.json b/server/package.json deleted file mode 100644 index e6329c3..0000000 --- a/server/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "mpvremote-server", - "private": true, - "version": "0.0.1", - "description": "", - "repository": "indefero@ghostdub.de:mpvremote.git", - "licenses": [ - { - "type": "gpl" - } - ], - "dependencies": { - "bunyan": "^1.8.0", - "every-moment": "0.0.1", - "nesh": "1.6.0", - "nodemon": "1.9.1", - "q": "1.4.1", - "q-io": "1.13.2", - "restify": "4.0.4", - "restify-plugins": "1.0.2" - }, - "scripts": { - "start": "./node_modules/.bin/nodemon server.js | ./node_modules/.bin/bunyan" - } -} diff --git a/server/server.js b/server/server.js deleted file mode 100644 index 5dcf4e3..0000000 --- a/server/server.js +++ /dev/null @@ -1,253 +0,0 @@ -var restify = require('restify'); -var bunyan = require('bunyan'); -var Q = require("q"); -var FS = require("q-io/fs"); -const net = require('net'); -var util = require("util"); - -String.prototype.format = function() { - var formatted = this; - for (var i = 0; i < arguments.length; i++) { - var regexp = new RegExp('\\{'+i+'\\}', 'gi'); - formatted = formatted.replace(regexp, arguments[i]); - } - return formatted; -}; - -// CONNECTIONS AND STUF -var socketPath = '/home/daddel9/.mpv-sock'; -var log = bunyan.createLogger({name: 'MpvRemote'}); - -// SETUP -var server = restify.createServer(); -server.pre(restify.pre.sanitizePath()); - - -// Serve static files -server.get(/\/client\/?.*/, restify.serveStatic({ - directory: __dirname -})); - - -// Cödê - - -global.mpvstate = { - "percent-pos":null, - "time-pos":null, - "time-remaining":null, - "chapter":null, - "chapter-list/count":null, - "pause":null, - "volume":null, - "mute":null, -}; -global.mpvisWaitingFor = null; - -global.mpvsocket = null; -global.mpvisConnected = false; -global.mpvconnect = function() { - defer = Q.defer(); - - log.info("socket is {0} isConnected is {1}".format(global.mpvsocket, global.mpvisConnected)); - if(global.mpvisConnected) { - defer.resolve(global.mpvsocket); - return defer.promise; - } - - sock = net.connect(socketPath); - - sock.on("connect", function() { - log.info("socket had connect event"); - global.mpvsocket = sock; - global.mpvisConnected=true; - defer.resolve(sock) - }); - sock.on("error", function(err) { - global.mpvisConnected=false; - defer.reject(err) - }); - sock.on("end", function() { - global.mpvisConnected=false; - defer.reject(new Error("socket ended")) - }); - sock.on("data", function(dta) { - log.info("had data '{0}' while waitingfor {1}".format(dta, global.mpvisWaitingFor)); - if(global.mpvisWaitingFor) { - dta = JSON.parse(dta); - var property = global.mpvisWaitingFor[0]; - var defer = global.mpvisWaitingFor[1]; - log.info("was waitingfor {0}, had data {1}, resolving now".format(property, dta['data'])); - global.mpvstate[property] = dta['data']; - global.mpvisWaitingFor = null; - defer.resolve(); - } - }); - return defer.promise; -} - - -function buildCommand(cmd, params) { - paramsStr=""; - params.forEach(function(val, idx, arr) { - paramsStr = paramsStr+', "{0}"'.format(val); - }) - ret = '{ "command": [ "{0}"{1} ] }\n'.format(cmd, paramsStr); - return ret; -} - -function writeCommand(cmd, params) { - defer = Q.defer(); - global.mpvconnect() - .then( function(sock) { - log.info("can write in "+sock); - try { - cmdStr = buildCommand(cmd, params); - log.info("trying to write cmd '{0}' to sock".format(cmdStr)); - sock.write(cmdStr); - defer.resolve(); - } catch(e) { - log.error("got error "+e); - defer.reject(e); - } - }, function(reason) { - defer.reject(reason); - }); - return defer.promise; -} - - -function updateProperty(propertyIdx) { - var defer = Q.defer(); - var keys = Object.keys(global.mpvstate); - var property = keys[propertyIdx]; - log.info("updating property at idx {0} which is {1}".format(propertyIdx, property)); - - writeCommand("get_property", [property]).then(function(){ - global.mpvisWaitingFor=[property,defer]; - - if(propertyIdx >= keys.length-1) { - log.info("idx too large, returning"); - return; - } else { - log.info("deferring updateProperty({0})".format(propertyIdx+1)); - defer.promise.then(function() { updateProperty(propertyIdx+1) } ); - } - }); - -} - -function stateUpdate() { - log.info("updating state"); - updateProperty(0); -} - -var every = require('every-moment'); -var timer = every(10, 'second', function() { - stateUpdate(); -}); - -var timer = every(12, 'second', function() { - log.info("my current state is: "+util.inspect(global.mpvstate)); -}); - -// /seekTime/:time — seeks relative -// /seekPercent/:percent — seeks percent absolute -// /seekChapter/:where — seeks to prev (where=-x) or next (where=+x) chapter -// /volume/:amount — increases (amount=+x) or decreases (amount=-x) volume -// /progress — show osd progress -// /playpause — toggles playing -// /muteunmute — toggles muting -// /stateUpdate — update state -// /stateVar/:name — get value of tracked property with name - -server.get('/seek/:time', function (req, res, next) { - log.info("seek "+req.params.time); - writeCommand("seek", [req.params.time]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/seekPercent/:percent', function (req, res, next) { - log.info("seekpercent "+req.params.percent); - writeCommand("seek", [req.params.percent, "absolute-percent"]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/seekChapter/:where', function (req, res, next) { - log.info("seekChapter "+req.params.where); - writeCommand("add", ["chapter", req.params.where]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -});server.get('/volume/:amount', function (req, res, next) { - log.info("volume "+req.params.amount); - writeCommand("add", ["volume", req.params.amount]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/volume/:amount', function (req, res, next) { - log.info("volume "+req.params.amount); - writeCommand("add", ["volume", req.params.amount]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/playpause', function (req, res, next) { - log.info("playpause "); - writeCommand("cycle", ["pause"]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/muteunmute', function (req, res, next) { - log.info("muteunmute "); - writeCommand("cycle", ["mute"]) - .then(function() { - res.send({"success":true}); - return next(false); - }, function(reason) { - res.send({"success":false, "reason": reason}); - return next(false); - }); -}); - -server.get('/stateUpdate', function (req, res, next) { - log.info("stateUpdate "); - stateUpdate(); -}); - -server.listen(8080, function() { - console.log('%s listening at %s', server.name, server.url); -}); From 21dc6257b823c10f4f4e45db1ca46f7d1eb32287 Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 14:37:13 +0100 Subject: [PATCH 2/6] Add stolen websocketserver --- server/SimpleWebSocketServer.py | 694 ++++++++++++++++++++++++++++++++ 1 file changed, 694 insertions(+) create mode 100644 server/SimpleWebSocketServer.py diff --git a/server/SimpleWebSocketServer.py b/server/SimpleWebSocketServer.py new file mode 100644 index 0000000..c505e60 --- /dev/null +++ b/server/SimpleWebSocketServer.py @@ -0,0 +1,694 @@ +''' +The MIT License (MIT) +Copyright (c) 2013 Dave P. +''' +import sys +VER = sys.version_info[0] +if VER >= 3: + import socketserver + from http.server import BaseHTTPRequestHandler + from io import StringIO, BytesIO +else: + import SocketServer + from BaseHTTPServer import BaseHTTPRequestHandler + from StringIO import StringIO + +import hashlib +import base64 +import socket +import struct +import ssl +import errno +import codecs +from collections import deque +from select import select + +__all__ = ['WebSocket', + 'SimpleWebSocketServer', + 'SimpleSSLWebSocketServer'] + +def _check_unicode(val): + if VER >= 3: + return isinstance(val, str) + else: + return isinstance(val, unicode) + +class HTTPRequest(BaseHTTPRequestHandler): + def __init__(self, request_text): + if VER >= 3: + self.rfile = BytesIO(request_text) + else: + self.rfile = StringIO(request_text) + self.raw_requestline = self.rfile.readline() + self.error_code = self.error_message = None + self.parse_request() + +_VALID_STATUS_CODES = [1000, 1001, 1002, 1003, 1007, 1008, + 1009, 1010, 1011, 3000, 3999, 4000, 4999] + +HANDSHAKE_STR = ( + "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" +) + +GUID_STR = '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 + +MAXHEADER = 65536 +MAXPAYLOAD = 33554432 + +class WebSocket(object): + + def __init__(self, server, sock, address): + self.server = server + self.client = sock + self.address = address + + self.handshaked = False + self.headerbuffer = bytearray() + self.headertoread = 2048 + + self.fin = 0 + self.data = bytearray() + 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.frag_start = False + self.frag_type = BINARY + self.frag_buffer = None + self.frag_decoder = codecs.getincrementaldecoder('utf-8')(errors='strict') + self.closed = False + self.sendq = deque() + + self.state = HEADERB1 + + # restrict the size of header and payload for security reasons + self.maxheader = MAXHEADER + self.maxpayload = MAXPAYLOAD + + def handleMessage(self): + """ + Called when websocket frame is received. + To access the frame data call self.data. + + If the frame is Text then self.data is a unicode object. + If the frame is Binary then self.data is a bytearray object. + """ + pass + + def handleConnected(self): + """ + Called when a websocket client connects to the server. + """ + pass + + def handleClose(self): + """ + Called when a websocket server gets a Close frame from a client. + """ + pass + + def _handlePacket(self): + if self.opcode == CLOSE: + pass + elif self.opcode == STREAM: + pass + elif self.opcode == TEXT: + pass + elif self.opcode == BINARY: + pass + elif self.opcode == PONG or self.opcode == PING: + if len(self.data) > 125: + raise Exception('control frame length can not be > 125') + else: + # unknown or reserved opcode so just close + raise Exception('unknown opcode') + + if self.opcode == CLOSE: + status = 1000 + reason = u'' + length = len(self.data) + + if length == 0: + pass + elif length >= 2: + status = struct.unpack_from('!H', self.data[:2])[0] + reason = self.data[2:] + + if status not in _VALID_STATUS_CODES: + status = 1002 + + if len(reason) > 0: + try: + reason = reason.decode('utf8', errors='strict') + except: + status = 1002 + else: + status = 1002 + + self.close(status, reason) + return + + elif self.fin == 0: + if self.opcode != STREAM: + if self.opcode == PING or self.opcode == PONG: + raise Exception('control messages can not be fragmented') + + self.frag_type = self.opcode + self.frag_start = True + self.frag_decoder.reset() + + if self.frag_type == TEXT: + self.frag_buffer = [] + utf_str = self.frag_decoder.decode(self.data, final = False) + if utf_str: + self.frag_buffer.append(utf_str) + else: + self.frag_buffer = bytearray() + self.frag_buffer.extend(self.data) + + else: + if self.frag_start is False: + raise Exception('fragmentation protocol error') + + if self.frag_type == TEXT: + utf_str = self.frag_decoder.decode(self.data, final = False) + if utf_str: + self.frag_buffer.append(utf_str) + else: + self.frag_buffer.extend(self.data) + + else: + if self.opcode == STREAM: + if self.frag_start is False: + raise Exception('fragmentation protocol error') + + if self.frag_type == TEXT: + utf_str = self.frag_decoder.decode(self.data, final = True) + self.frag_buffer.append(utf_str) + self.data = u''.join(self.frag_buffer) + else: + self.frag_buffer.extend(self.data) + self.data = self.frag_buffer + + self.handleMessage() + + self.frag_decoder.reset() + self.frag_type = BINARY + self.frag_start = False + self.frag_buffer = None + + elif self.opcode == PING: + self._sendMessage(False, PONG, self.data) + + elif self.opcode == PONG: + pass + + else: + if self.frag_start is True: + raise Exception('fragmentation protocol error') + + if self.opcode == TEXT: + try: + self.data = self.data.decode('utf8', errors='strict') + except Exception as exp: + raise Exception('invalid utf-8 payload') + + self.handleMessage() + + + def _handleData(self): + # do the HTTP header and handshake + if self.handshaked is False: + + data = self.client.recv(self.headertoread) + if not data: + raise Exception('remote socket closed') + + else: + # accumulate + self.headerbuffer.extend(data) + + if len(self.headerbuffer) >= self.maxheader: + raise Exception('header exceeded allowable size') + + # indicates end of HTTP header + if b'\r\n\r\n' in self.headerbuffer: + self.request = HTTPRequest(self.headerbuffer) + + # handshake rfc 6455 + try: + key = self.request.headers['Sec-WebSocket-Key'] + k = key.encode('ascii') + GUID_STR.encode('ascii') + k_s = base64.b64encode(hashlib.sha1(k).digest()).decode('ascii') + hStr = HANDSHAKE_STR % {'acceptstr': k_s} + self.sendq.append((BINARY, hStr.encode('ascii'))) + self.handshaked = True + self.handleConnected() + except Exception as e: + raise Exception('handshake failed: %s', str(e)) + + # else do normal data + else: + data = self.client.recv(8192) + if not data: + raise Exception("remote socket closed") + + if VER >= 3: + for d in data: + self._parseMessage(d) + else: + for d in data: + self._parseMessage(ord(d)) + + def close(self, status = 1000, reason = u''): + """ + Send Close frame to the client. The underlying socket is only closed + when the client acknowledges the Close frame. + + status is the closing identifier. + reason is the reason for the close. + """ + try: + if self.closed is False: + close_msg = bytearray() + close_msg.extend(struct.pack("!H", status)) + if _check_unicode(reason): + close_msg.extend(reason.encode('utf-8')) + else: + close_msg.extend(reason) + + self._sendMessage(False, CLOSE, close_msg) + + finally: + self.closed = True + + + def _sendBuffer(self, buff): + size = len(buff) + tosend = size + already_sent = 0 + + while tosend > 0: + try: + # i should be able to send a bytearray + sent = self.client.send(buff[already_sent:]) + if sent == 0: + raise RuntimeError('socket connection broken') + + already_sent += 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 in [errno.EAGAIN, errno.EWOULDBLOCK]: + return buff[already_sent:] + else: + raise e + + return None + + def sendFragmentStart(self, data): + """ + Send the start of a data fragment stream to a websocket client. + Subsequent data should be sent using sendFragment(). + A fragment stream is completed when sendFragmentEnd() is called. + + If data is a unicode object then the frame is sent as Text. + If the data is a bytearray object then the frame is sent as Binary. + """ + opcode = BINARY + if _check_unicode(data): + opcode = TEXT + self._sendMessage(True, opcode, data) + + def sendFragment(self, data): + """ + see sendFragmentStart() + + If data is a unicode object then the frame is sent as Text. + If the data is a bytearray object then the frame is sent as Binary. + """ + self._sendMessage(True, STREAM, data) + + def sendFragmentEnd(self, data): + """ + see sendFragmentEnd() + + If data is a unicode object then the frame is sent as Text. + If the data is a bytearray object then the frame is sent as Binary. + """ + self._sendMessage(False, STREAM, data) + + def sendMessage(self, data): + """ + Send websocket data frame to the client. + + If data is a unicode object then the frame is sent as Text. + If the data is a bytearray object then the frame is sent as Binary. + """ + opcode = BINARY + if _check_unicode(data): + opcode = TEXT + self._sendMessage(False, opcode, data) + + + def _sendMessage(self, fin, opcode, data): + + payload = bytearray() + + b1 = 0 + b2 = 0 + if fin is False: + b1 |= 0x80 + b1 |= opcode + + if _check_unicode(data): + data = data.encode('utf-8') + + length = len(data) + payload.append(b1) + + if length <= 125: + b2 |= length + payload.append(b2) + + elif length >= 126 and length <= 65535: + b2 |= 126 + payload.append(b2) + payload.extend(struct.pack("!H", length)) + + else: + b2 |= 127 + payload.append(b2) + payload.extend(struct.pack("!Q", length)) + + if length > 0: + payload.extend(data) + + self.sendq.append((opcode, payload)) + + + def _parseMessage(self, byte): + # read in the header + if self.state == HEADERB1: + + self.fin = byte & 0x80 + self.opcode = byte & 0x0F + self.state = HEADERB2 + + self.index = 0 + self.length = 0 + self.lengtharray = bytearray() + self.data = bytearray() + + rsv = byte & 0x70 + if rsv != 0: + raise Exception('RSV bit must be 0') + + elif self.state == HEADERB2: + mask = byte & 0x80 + length = byte & 0x7F + + if self.opcode == PING and length > 125: + raise Exception('ping packet is too large') + + 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 = 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 = bytearray() + + # we have no mask and some payload + else: + #self.index = 0 + self.data = bytearray() + self.state = PAYLOAD + + elif length == 126: + self.lengtharray = bytearray() + self.state = LENGTHSHORT + + elif length == 127: + self.lengtharray = bytearray() + self.state = LENGTHLONG + + + elif self.state == 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', self.lengtharray)[0] + + if self.hasmask is True: + self.maskarray = bytearray() + self.state = MASK + else: + # if there is no mask and no payload we are done + if self.length <= 0: + try: + self._handlePacket() + finally: + self.state = HEADERB1 + self.data = bytearray() + + # we have no mask and some payload + else: + #self.index = 0 + self.data = bytearray() + self.state = PAYLOAD + + elif self.state == 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', self.lengtharray)[0] + + if self.hasmask is True: + self.maskarray = bytearray() + self.state = MASK + else: + # if there is no mask and no payload we are done + if self.length <= 0: + try: + self._handlePacket() + finally: + self.state = HEADERB1 + self.data = bytearray() + + # we have no mask and some payload + else: + #self.index = 0 + self.data = bytearray() + self.state = PAYLOAD + + # MASK STATE + elif self.state == 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 = HEADERB1 + self.data = bytearray() + + # we have no mask and some payload + else: + #self.index = 0 + self.data = bytearray() + self.state = PAYLOAD + + # PAYLOAD STATE + elif self.state == 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.index = 0 + self.state = HEADERB1 + self.data = bytearray() + else: + self.index += 1 + + +class SimpleWebSocketServer(object): + def __init__(self, host, port, websocketclass, selectInterval = 0.1): + 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.selectInterval = selectInterval + 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 desc, conn in self.connections.items(): + conn.close() + conn.handleClose() + + + def serveforever(self): + while True: + writers = [] + for fileno in self.listeners: + if fileno == self.serversocket: + continue + client = self.connections[fileno] + if client.sendq: + writers.append(fileno) + + if self.selectInterval: + rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval) + else: + rList, wList, xList = select(self.listeners, writers, self.listeners) + + for ready in wList: + client = self.connections[ready] + try: + while client.sendq: + opcode, payload = client.sendq.popleft() + remaining = client._sendBuffer(payload) + if remaining is not None: + client.sendq.appendleft((opcode, remaining)) + break + else: + if opcode == CLOSE: + raise Exception('received client close') + + except Exception as n: + client.client.close() + client.handleClose() + del self.connections[ready] + self.listeners.remove(ready) + + 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.connections[fileno] = self._constructWebSocket(newsock, address) + self.listeners.append(fileno) + except Exception as n: + if sock is not None: + sock.close() + else: + if ready not in self.connections: + continue + client = self.connections[ready] + try: + client._handleData() + except Exception as n: + client.client.close() + client.handleClose() + del self.connections[ready] + self.listeners.remove(ready) + + for failed in xList: + if failed == self.serversocket: + self.close() + raise Exception('server socket failed') + else: + if failed not in self.connections: + continue + client = self.connections[failed] + client.client.close() + client.handleClose() + del self.connections[failed] + self.listeners.remove(failed) + + +class SimpleSSLWebSocketServer(SimpleWebSocketServer): + + def __init__(self, host, port, websocketclass, certfile, + keyfile, version = ssl.PROTOCOL_TLSv1, selectInterval = 0.1): + + SimpleWebSocketServer.__init__(self, host, port, + websocketclass, selectInterval) + + self.context = ssl.SSLContext(version) + self.context.load_cert_chain(certfile, keyfile) + + def close(self): + super(SimpleSSLWebSocketServer, self).close() + + def _decorateSocket(self, sock): + sslsock = self.context.wrap_socket(sock, server_side=True) + 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() From 580da93ee424574607ad3f6926773a9cf290249d Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 17:25:36 +0100 Subject: [PATCH 3/6] Able to update playerState from socket --- server/SimpleWebSocketServer.pyc | Bin 0 -> 17436 bytes server/server.py | 131 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 server/SimpleWebSocketServer.pyc create mode 100644 server/server.py diff --git a/server/SimpleWebSocketServer.pyc b/server/SimpleWebSocketServer.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63c958bdca03216497da99cb1669ef69d47512bf GIT binary patch literal 17436 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≶7VoE93Wj?6XfIr8u z1PS&!Jh#`)?8a#d+Oe047Q-q^HKqQ9y}6VzCX7&>(@t|;@4CT*+sTfu!%xJ(xC^1- z5x(GBnVg1jOk4^>4czz>#h}ahWQt64^=8-p%FZSgBap z81&J}@!I1{Hgf17XVWF4Y+_sxVxJIV78SA)zL$>heX- z>s}3fCD~8KZ_`EKx3w_M4;jB>f zAVuO-ycaOLE}oqf%ju~Y^>TM2z38h-F1&*r2(&uT+!1$z1ZP?iAA)2p>gNx5d82e zq$)oS3mfj2Q6z?#T5V$L5|679#9S1+Ut>8(644h;LjOwYYBRObOv#y|#7@wCjg##Vn=O+AN9;Zfk4qGx8yKJZxD&dVx4L-iY7b1= z`|V?5X!Cv-jAXzeD-ZK|AB@V+a@w@E5bo>2=M%X?cMWgy%K@In6LUe-ru9Wp zg6V15;_E8dv1-{$*zRRmw($7XvXzi8bGmP}Y^kdFG_@0lfoh`8fFuEg#1c_|gZD*c zP;7f_XcV^_xD2!*5N)R)JE~GL{*8;x?l27L>50`%#Y{w^VBQ zxCWnpf*sq!v9GbeFA~rU6fczZxI3g5-1KRtX~M#`++JJpt6U_~%fOxL!Og-NC(4IQ zG>S)#DM3K&AW#VOfEK@jDp;w^AY)b8mht+kAGzGXA{q2!VQy;SCjsw)smP~)LVWQ6 z0kLK|bRLgjdI$;N2(wWw1aJ}-SZ~8)+0!ppQ+Y&}pc3Q4jG9q@jwWPJuhaKv1=PDn)c;ad_JO^V!J#HmWHHi9{kN3h}oE zQFi$)BFf1dt5{&~ExGtjyoz&o!JVF&9)(vpGg-PaEFbTxs^em*(A-{m$u0ATL`>vm zZm=8DGGFK@a`lAcDiyC$!)L`LXS^AUj!6XqK1`l(=q*BVcE!C?b3hn+F-cZW={paI zxFz6KH{{c7{ajm|UTId+Pbd3O<81nW2hc3HhAg*AfJHQ}1gc0c_X@!W0Aw(HD9o?_ z=>yB>f-TdcQo#)pd@}dLR|rK(5DYo` pH-MZRv^7F%okKl?*1=q7XQ!No@kERllhS{)v_14Q^OK?K{{ljw*0KNq literal 0 HcmV?d00001 diff --git a/server/server.py b/server/server.py new file mode 100644 index 0000000..81a3591 --- /dev/null +++ b/server/server.py @@ -0,0 +1,131 @@ +from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket + +import json +import socket +import os +import threading +import select +import random + +SOCKPATH='/home/dario/.mpv.sock' +global mpvParser +mpvParser = None + +class SocketPoller(threading.Thread): + def __init__(self, sockParser): + super(SocketPoller, self).__init__() + self.sockParser = sockParser + self.stop=False + + if not os.path.exists(SOCKPATH): + raise Exception("Could not find server socket ~/.mpv.sock, exiting") + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.connect(SOCKPATH) + self.buf="" + + def run(self): + while not self.stop: + if len( select.select([self.sock], [], [])[0] ) > 0: + self.buf += self.sock.recv(1) + if '\n' in self.buf: + self.sockParser.newData(self.buf) + self.buf="" + + def sendData(self, data): + if not '\n' in data: + data+='\n' + print "sending data '%s'"%data + self.sock.sendall(data) + +class MpvSockParser(object): + def __init__(self): + self.listeners = [] + self.playerState = { + "totalDuration": 42, + "currentDuration": 23, + "volume": 66, + "fileName": "No Data Yet", + "isPlaying": False + } + self.poller = None + self.waitingForCommand = None + + def registerListener(self, listener): + self.listeners.append(listener) + def unregisterListener(self, listener): + self.listeners.remove(listener) + def notifyListeners(self): + for l in self.listeners: + l.dataNotify(self.playerState) + + def startPoll(self): + self.poller = SocketPoller(self) + + self.poller.sendData('{"command":["observe_property",1,"time-pos"]}') + self.poller.sendData('{"command":["observe_property",2,"volume"]}') + self.poller.sendData('{"command":["observe_property",3,"filename"]}') + self.poller.sendData('{"command":["observe_property",4,"duration"]}') + + self.poller.run() + + def newData(self, data): + data = json.loads(data) + # mpv-generated event + if "event" in data: + self.processEvent(data) + # answer to our request + if 'error' in data: + self.processCommand(data) + + def processEvent(self, data): + sendNotify = False + if "data" in data and data["data"]==None: + return + + print data + if data['event'] == 'property-change' and data['name'] == 'time-pos': + self.playerState['currentDuration'] = int(data['data']) + if data['event'] == 'property-change' and data['name'] == 'volume': + self.playerState['volume'] = int(data['data']) + if data['event'] == 'property-change' and data['name'] == 'filename': + self.playerState['fileName'] = data['data'] + if data['event'] == 'property-change' and data['name'] == 'duration': + self.playerState['totalDuration'] = int(data['data']) + if data['event'] == 'pause': + self.playerState['isPlaying'] = False + if data['event'] == 'unpause': + self.playerState['isPlaying'] = True + + print self.playerState + self.notifyListeners() + + def processCommand(self, data): + pass + def sendCommand(self, command): + if command[command] == "seek": + pass + +class WSHandler(WebSocket): + def handleMessage(self): + #self.sendMessage(self.data) + print "got message '%s'"%(self.data) + def handleConnected(self): + global mpvParser + mpvParser.registerListener(self) + print self.address, 'connected' + def handleClose(self): + global mpvParser + print self.address, 'closed' + mpvParser.unregisterListener(self) + def dataNotify(self, data): + data['type'] = 'status' + self.sendMessage(data) + + +if __name__=='__main__': + global mpvParser + mpvParser = MpvSockParser() + mpvParser.startPoll() + + server = SimpleWebSocketServer('', 8000, SimpleEcho) + server.serveforever() From 65a4618c2afb0600c797c1b5d982b8dd4e623a2d Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 18:27:21 +0100 Subject: [PATCH 4/6] first command implemented --- server/server.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/server/server.py b/server/server.py index 81a3591..bf9ad73 100644 --- a/server/server.py +++ b/server/server.py @@ -32,9 +32,9 @@ class SocketPoller(threading.Thread): self.buf="" def sendData(self, data): + print "sending data '%s'"%data if not '\n' in data: data+='\n' - print "sending data '%s'"%data self.sock.sendall(data) class MpvSockParser(object): @@ -66,7 +66,7 @@ class MpvSockParser(object): self.poller.sendData('{"command":["observe_property",3,"filename"]}') self.poller.sendData('{"command":["observe_property",4,"duration"]}') - self.poller.run() + self.poller.start() def newData(self, data): data = json.loads(data) @@ -102,13 +102,23 @@ class MpvSockParser(object): def processCommand(self, data): pass def sendCommand(self, command): - if command[command] == "seek": - pass + print "sending command %s"%command + if command["command"] == "play": + self.poller.sendData( + '{ "command": ["set_property", "pause", true] }' + ) + if command["command"] == "pause": + self.poller.sendData( + '{ "command": ["set_property", "pause", false] }' + ) class WSHandler(WebSocket): def handleMessage(self): - #self.sendMessage(self.data) - print "got message '%s'"%(self.data) + global mpvParser + command = json.loads(self.data) + print "got command %s"%(command) + mpvParser.sendCommand(command) + self.sendMessage('{"null":""}') def handleConnected(self): global mpvParser mpvParser.registerListener(self) @@ -119,7 +129,7 @@ class WSHandler(WebSocket): mpvParser.unregisterListener(self) def dataNotify(self, data): data['type'] = 'status' - self.sendMessage(data) + self.sendMessage(unicode( json.dumps(data) )) if __name__=='__main__': @@ -127,5 +137,5 @@ if __name__=='__main__': mpvParser = MpvSockParser() mpvParser.startPoll() - server = SimpleWebSocketServer('', 8000, SimpleEcho) + server = SimpleWebSocketServer('', 8000, WSHandler) server.serveforever() From 40b6115ee1d1502782c510b50de6115a1d1b884f Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 18:32:20 +0100 Subject: [PATCH 5/6] spam throttling for time-pos --- server/server.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/server/server.py b/server/server.py index bf9ad73..25891f4 100644 --- a/server/server.py +++ b/server/server.py @@ -78,13 +78,16 @@ class MpvSockParser(object): self.processCommand(data) def processEvent(self, data): - sendNotify = False + sendNotify = True if "data" in data and data["data"]==None: return - print data if data['event'] == 'property-change' and data['name'] == 'time-pos': - self.playerState['currentDuration'] = int(data['data']) + oldCur = self.playerState['currentDuration'] + curCur = int(data['data']) + if curCur - oldCur < 1: + sendNotify = False + self.playerState['currentDuration'] = curCur if data['event'] == 'property-change' and data['name'] == 'volume': self.playerState['volume'] = int(data['data']) if data['event'] == 'property-change' and data['name'] == 'filename': @@ -96,27 +99,22 @@ class MpvSockParser(object): if data['event'] == 'unpause': self.playerState['isPlaying'] = True - print self.playerState - self.notifyListeners() + if sendNotify: + print self.playerState + self.notifyListeners() def processCommand(self, data): pass def sendCommand(self, command): - print "sending command %s"%command if command["command"] == "play": - self.poller.sendData( - '{ "command": ["set_property", "pause", true] }' - ) + self.poller.sendData('{ "command": ["set_property", "pause", true] }') if command["command"] == "pause": - self.poller.sendData( - '{ "command": ["set_property", "pause", false] }' - ) + self.poller.sendData('{ "command": ["set_property", "pause", false] }') class WSHandler(WebSocket): def handleMessage(self): global mpvParser command = json.loads(self.data) - print "got command %s"%(command) mpvParser.sendCommand(command) self.sendMessage('{"null":""}') def handleConnected(self): From 5141ad90505314748460cb852bbf8995623537dd Mon Sep 17 00:00:00 2001 From: Dario Ernst Date: Tue, 27 Dec 2016 18:55:22 +0100 Subject: [PATCH 6/6] more untested commands and pause state sync --- server/server.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/server/server.py b/server/server.py index 25891f4..8c9a4f6 100644 --- a/server/server.py +++ b/server/server.py @@ -65,6 +65,8 @@ class MpvSockParser(object): self.poller.sendData('{"command":["observe_property",2,"volume"]}') self.poller.sendData('{"command":["observe_property",3,"filename"]}') self.poller.sendData('{"command":["observe_property",4,"duration"]}') + self.poller.sendData('{"command":["observe_property",4,"duration"]}') + self.poller.sendData('{ "command": ["get_property", "pause"], "request_id":10}') self.poller.start() @@ -104,19 +106,32 @@ class MpvSockParser(object): self.notifyListeners() def processCommand(self, data): - pass + if "request_id" in data and data['request_id'] == 10: + print 'synced pause state' + self.playerState['isPlaying'] = not data["data"] + def sendCommand(self, command): if command["command"] == "play": - self.poller.sendData('{ "command": ["set_property", "pause", true] }') - if command["command"] == "pause": self.poller.sendData('{ "command": ["set_property", "pause", false] }') + if command["command"] == "pause": + self.poller.sendData('{ "command": ["set_property", "pause", true] }') + if command["command"] == "seek": + self.poller.sendData('{ "command": ["seek", "%s", "absolute"] }'%command['seekValue']) + if command["command"] == "volume": + self.poller.sendData('{ "command": ["set", "volume", %s] }'%command['volume']) + if command["command"] == "seekChapter": + if command['direction'] == 'forward': + direction = +1 + elif command['direction'] == 'backward': + direction = -1 + self.poller.sendData('{ "command": ["add", "chapter", %s] }'%direction) class WSHandler(WebSocket): def handleMessage(self): global mpvParser command = json.loads(self.data) + print 'got commadn %s'%command mpvParser.sendCommand(command) - self.sendMessage('{"null":""}') def handleConnected(self): global mpvParser mpvParser.registerListener(self)