From a52aa9f4b5eabf91204c0cfd4c27685c3f1be7ea Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:29:49 +0000 Subject: [PATCH] Moved api out to server Reworked sockets to use socketio Added progress to nodes Added highlight to active node Added preview to saveimage node --- main.py | 247 ++++++++++----------------------------- nodes.py | 12 +- requirements.txt | 2 +- server.py | 173 +++++++++++++++++++++++++++ webshit/index.html | 188 ++++++++++++++++++++++------- webshit/socket.io.min.js | 7 ++ 6 files changed, 393 insertions(+), 236 deletions(-) create mode 100644 server.py create mode 100644 webshit/socket.io.min.js diff --git a/main.py b/main.py index 7c72bc4e..283b0bd2 100644 --- a/main.py +++ b/main.py @@ -11,15 +11,7 @@ if os.name == "nt": import logging logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage()) -try: - import aiohttp - from aiohttp import web -except ImportError: - print("Module 'aiohttp' not installed. Please install it via:") - print("pip install aiohttp") - print("or") - print("pip install -r requirements.txt") - sys.exit() +import server if __name__ == "__main__": if '--help' in sys.argv: @@ -36,14 +28,14 @@ if __name__ == "__main__": print() exit() -if '--dont-upcast-attention' in sys.argv: - print("disabling upcasting of attention") - os.environ['ATTN_PRECISION'] = "fp16" + if '--dont-upcast-attention' in sys.argv: + print("disabling upcasting of attention") + os.environ['ATTN_PRECISION'] = "fp16" import torch import nodes -def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}): +def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}, server=None, unique_id=None): valid_inputs = class_def.INPUT_TYPES() input_data_all = {} for x in inputs: @@ -65,9 +57,13 @@ def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}): if h[x] == "EXTRA_PNGINFO": if "extra_pnginfo" in extra_data: input_data_all[x] = extra_data['extra_pnginfo'] + if h[x] == "SERVER": + input_data_all[x] = server + if h[x] == "UNIQUE_ID": + input_data_all[x] = unique_id return input_data_all -def recursive_execute(prompt, outputs, current_item, extra_data={}): +def recursive_execute(server, prompt, outputs, current_item, extra_data={}): unique_id = current_item inputs = prompt[unique_id]['inputs'] class_type = prompt[unique_id]['class_type'] @@ -84,9 +80,11 @@ def recursive_execute(prompt, outputs, current_item, extra_data={}): input_unique_id = input_data[0] output_index = input_data[1] if input_unique_id not in outputs: - executed += recursive_execute(prompt, outputs, input_unique_id, extra_data) + executed += recursive_execute(server, prompt, outputs, input_unique_id, extra_data) - input_data_all = get_input_data(inputs, class_def, outputs, prompt, extra_data) + input_data_all = get_input_data(inputs, class_def, outputs, prompt, extra_data, server, unique_id) + if server.client_id is not None: + server.send_sync("execute", { "node": unique_id }, server.client_id) obj = class_def() outputs[unique_id] = getattr(obj, obj.FUNCTION)(**input_data_all) @@ -157,11 +155,17 @@ def recursive_output_delete_if_changed(prompt, old_prompt, outputs, current_item return to_delete class PromptExecutor: - def __init__(self): + def __init__(self, server): self.outputs = {} self.old_prompt = {} + self.server = server def execute(self, prompt, extra_data={}): + if "client_id" in extra_data: + self.server.client_id = extra_data["client_id"] + else: + self.server.client_id = None + with torch.no_grad(): for x in prompt: recursive_output_delete_if_changed(prompt, self.old_prompt, self.outputs, x) @@ -190,7 +194,7 @@ class PromptExecutor: except: valid = False if valid: - executed += recursive_execute(prompt, self.outputs, x, extra_data) + executed += recursive_execute(self.server, prompt, self.outputs, x, extra_data) except Exception as e: print(traceback.format_exc()) @@ -208,6 +212,11 @@ class PromptExecutor: executed = set(executed) for x in executed: self.old_prompt[x] = copy.deepcopy(prompt[x]) + + finally: + if self.server.client_id is not None: + self.server.send_sync("execute", { "node": None }, self.server.client_id) + torch.cuda.empty_cache() def validate_inputs(prompt, item): @@ -293,27 +302,27 @@ def validate_prompt(prompt): return (True, "") -def prompt_worker(q): - e = PromptExecutor() +def prompt_worker(q, server): + e = PromptExecutor(server) while True: item, item_id = q.get() e.execute(item[-2], item[-1]) q.task_done(item_id) class PromptQueue: - def __init__(self, socket_handler): - self.socket_handler = socket_handler + def __init__(self, server): + self.server = server self.mutex = threading.RLock() self.not_empty = threading.Condition(self.mutex) self.task_counter = 0 self.queue = [] self.currently_running = {} - socket_handler.prompt_queue = self + server.prompt_queue = self def put(self, item): with self.mutex: heapq.heappush(self.queue, item) - self.socket_handler.queue_updated(self) + self.server.queue_updated() self.not_empty.notify() def get(self): @@ -324,13 +333,13 @@ class PromptQueue: i = self.task_counter self.currently_running[i] = copy.deepcopy(item) self.task_counter += 1 - self.socket_handler.queue_updated(self) + self.server.queue_updated() return (item, i) def task_done(self, item_id): with self.mutex: self.currently_running.pop(item_id) - self.socket_handler.queue_updated(self) + self.server.queue_updated() def get_current_queue(self): with self.mutex: @@ -346,7 +355,7 @@ class PromptQueue: def wipe_queue(self): with self.mutex: self.queue = [] - self.socket_handler.queue_updated(self) + self.server.queue_updated() def delete_queue_item(self, function): with self.mutex: @@ -357,174 +366,32 @@ class PromptQueue: else: self.queue.pop(x) heapq.heapify(self.queue) - self.socket_handler.queue_updated(self) + self.server.queue_updated() return True return False -def get_queue_info(prompt_queue): - prompt_info = {} - exec_info = {} - exec_info['queue_remaining'] = prompt_queue.get_tasks_remaining() - prompt_info['exec_info'] = exec_info - return prompt_info +async def run(server, address='', port=8188): + await asyncio.gather(server.start(address, port), server.publish_loop()) -class SocketHandler(): - def __init__(self, loop): - self.connected = set() - self.messages = asyncio.Queue() - self.loop = loop - - async def publish_loop(self): - while True: - msg = await self.messages.get() - await self.send(msg) - - def queue_updated(self, queue): - # This is called by the queue processing thread so we need to make it thread safe - loop.call_soon_threadsafe(self.messages.put_nowait, { 'type': 'status', 'status': get_queue_info(queue) }) - - async def send(self, message, socket = None): - if isinstance(message, str) == False: - message = json.dumps(message) - - if socket is None: - for ws in self.connected: - await ws.send_str(message) - else: - await socket.send_str(message) - - async def process(self, request): - ws = web.WebSocketResponse() - await ws.prepare(request) - self.connected.add(ws) - try: - # Send initial state to the new client - await self.send({ 'type': 'status', 'status': get_queue_info(self.prompt_queue) }, ws) - async for msg in ws: - if msg.type == aiohttp.WSMsgType.ERROR: - print('ws connection closed with exception %s' % ws.exception()) - finally: - self.connected.remove(ws) - - return ws - -class PromptServer(): - def __init__(self, prompt_queue, socket_handler): - self.prompt_queue = prompt_queue - self.socket_handler = socket_handler - self.number = 0 - self.app = web.Application() - self.web_root = os.path.join(os.path.dirname(os.path.realpath(__file__)), "webshit") - routes = web.RouteTableDef() - - @routes.get('/ws') - async def websocket_handler(request): - return await self.socket_handler.process(request) - - @routes.get("/") - async def get_root(request): - return web.FileResponse(os.path.join(self.web_root, "index.html")) - - @routes.get("/prompt") - async def get_prompt(request): - return web.json_response(get_queue_info(self.prompt_queue)) - - @routes.get("/object_info") - async def get_object_info(request): - out = {} - for x in nodes.NODE_CLASS_MAPPINGS: - obj_class = nodes.NODE_CLASS_MAPPINGS[x] - info = {} - info['input'] = obj_class.INPUT_TYPES() - info['output'] = obj_class.RETURN_TYPES - info['name'] = x #TODO - info['description'] = '' - info['category'] = 'sd' - if hasattr(obj_class, 'CATEGORY'): - info['category'] = obj_class.CATEGORY - out[x] = info - return web.json_response(out) - - @routes.get("/queue") - async def get_queue(request): - queue_info = {} - current_queue = self.prompt_queue.get_current_queue() - queue_info['queue_running'] = current_queue[0] - queue_info['queue_pending'] = current_queue[1] - return web.json_response(queue_info) - - @routes.post("/prompt") - async def post_prompt(request): - print("got prompt") - resp_code = 200 - out_string = "" - json_data = await request.json() - - if "number" in json_data: - number = float(json_data['number']) - else: - number = self.number - if "front" in json_data: - if json_data['front']: - number = -number - - self.number += 1 - if "prompt" in json_data: - prompt = json_data["prompt"] - valid = validate_prompt(prompt) - extra_data = {} - if "extra_data" in json_data: - extra_data = json_data["extra_data"] - if valid[0]: - self.prompt_queue.put((number, id(prompt), prompt, extra_data)) - else: - resp_code = 400 - out_string = valid[1] - print("invalid prompt:", valid[1]) - - return web.Response(body=out_string, status=resp_code) - - @routes.post("/queue") - async def post_queue(request): - json_data = await request.json() - if "clear" in json_data: - if json_data["clear"]: - self.prompt_queue.wipe_queue() - if "delete" in json_data: - to_delete = json_data['delete'] - for id_to_delete in to_delete: - delete_func = lambda a: a[1] == int(id_to_delete) - self.prompt_queue.delete_queue_item(delete_func) - - return web.Response(status=200) - - self.app.add_routes(routes) - self.app.add_routes([ - web.static('/', self.web_root), - ]) - -async def start_server(server, address, port): - runner = web.AppRunner(server.app) - await runner.setup() - site = web.TCPSite(runner, address, port) - await site.start() - - if address == '': - address = '0.0.0.0' - print("Starting server\n") - print("To see the GUI go to: http://{}:{}".format(address, port)) - -async def run(prompt_queue, socket_handler, address='', port=8188): - server = PromptServer(prompt_queue, socket_handler) - await asyncio.gather(start_server(server, address, port), socket_handler.publish_loop()) +def hijack_progress(server): + from tqdm.auto import tqdm + orig_func = getattr(tqdm, "update") + def wrapped_func(*args, **kwargs): + pbar = args[0] + v = orig_func(*args, **kwargs) + server.send_sync("progress", { "value": pbar.n, "max": pbar.total}, server.client_id) + return v + setattr(tqdm, "update", wrapped_func) if __name__ == "__main__": loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) + server = server.PromptServer(loop) + q = PromptQueue(server) - socket_handler = SocketHandler(loop) - q = PromptQueue(socket_handler) - threading.Thread(target=prompt_worker, daemon=True, args=(q,)).start() + hijack_progress(server) + + threading.Thread(target=prompt_worker, daemon=True, args=(q,server,)).start() if '--listen' in sys.argv: address = '0.0.0.0' else: @@ -537,5 +404,11 @@ if __name__ == "__main__": except: pass - loop.run_until_complete(run(q, socket_handler, address=address, port=port)) + if os.name == "nt": + try: + loop.run_until_complete(run(server, address=address, port=port)) + except KeyboardInterrupt: + pass + else: + loop.run_until_complete(run(server, address=address, port=port)) diff --git a/nodes.py b/nodes.py index 3bdad71b..e307f6b8 100644 --- a/nodes.py +++ b/nodes.py @@ -605,7 +605,7 @@ class SaveImage: return {"required": {"images": ("IMAGE", ), "filename_prefix": ("STRING", {"default": "ComfyUI"})}, - "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, + "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "server": "SERVER", "unique_id": "UNIQUE_ID"}, } RETURN_TYPES = () @@ -615,7 +615,7 @@ class SaveImage: CATEGORY = "image" - def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None): + def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None, server=None, unique_id=None): def map_filename(filename): prefix_len = len(filename_prefix) prefix = filename[:prefix_len + 1] @@ -631,6 +631,8 @@ class SaveImage: except FileNotFoundError: os.mkdir(self.output_dir) counter = 1 + + paths = list() for image in images: i = 255. * image.cpu().numpy() img = Image.fromarray(i.astype(np.uint8)) @@ -640,8 +642,12 @@ class SaveImage: if extra_pnginfo is not None: for x in extra_pnginfo: metadata.add_text(x, json.dumps(extra_pnginfo[x])) - img.save(os.path.join(self.output_dir, f"{filename_prefix}_{counter:05}_.png"), pnginfo=metadata, optimize=True) + file = f"{filename_prefix}_{counter:05}_.png" + img.save(os.path.join(self.output_dir, file), pnginfo=metadata, optimize=True) + paths.append(f"/view/{file}") counter += 1 + if server is not None: + server.send_sync("image", {"images": paths, "id": unique_id}) class LoadImage: input_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "input") diff --git a/requirements.txt b/requirements.txt index e4be9ebc..f6656b9d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,4 @@ safetensors pytorch_lightning aiohttp accelerate - +python-socketio diff --git a/server.py b/server.py new file mode 100644 index 00000000..942d2495 --- /dev/null +++ b/server.py @@ -0,0 +1,173 @@ +import os +import sys +import asyncio +import nodes +import main + +try: + import aiohttp + from aiohttp import web +except ImportError: + print("Module 'aiohttp' not installed. Please install it via:") + print("pip install aiohttp") + print("or") + print("pip install -r requirements.txt") + sys.exit() + +try: + import socketio +except ImportError: + print("Module 'python-socketio' not installed. Please install it via:") + print("pip install python-socketio") + print("or") + print("pip install -r requirements.txt") + sys.exit() + + +class PromptServer(): + def __init__(self, loop): + self.prompt_queue = None + self.loop = loop + self.messages = asyncio.Queue() + self.number = 0 + self.app = web.Application() + self.sio = socketio.AsyncServer() + self.sio.attach(self.app) + self.web_root = os.path.join(os.path.dirname( + os.path.realpath(__file__)), "webshit") + routes = web.RouteTableDef() + + @self.sio.event + async def connect(sid, environ): + await self.sio.emit("status", self.get_queue_info(), sid) + + @routes.get("/") + async def get_root(request): + return web.FileResponse(os.path.join(self.web_root, "index.html")) + + @routes.get("/view/{file}") + async def view_image(request): + if "file" in request.match_info: + output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output") + file = request.match_info["file"] + file = os.path.splitext(os.path.basename(file))[0] + ".png" + file = os.path.join(output_dir, file) + if os.path.isfile(file): + return web.FileResponse(file) + + return web.Response(status=404) + + @routes.get("/prompt") + async def get_prompt(request): + return web.json_response(self.get_queue_info()) + + @routes.get("/object_info") + async def get_object_info(request): + out = {} + for x in nodes.NODE_CLASS_MAPPINGS: + obj_class = nodes.NODE_CLASS_MAPPINGS[x] + info = {} + info['input'] = obj_class.INPUT_TYPES() + info['output'] = obj_class.RETURN_TYPES + info['name'] = x #TODO + info['description'] = '' + info['category'] = 'sd' + if hasattr(obj_class, 'CATEGORY'): + info['category'] = obj_class.CATEGORY + out[x] = info + return web.json_response(out) + + @routes.get("/queue") + async def get_queue(request): + queue_info = {} + current_queue = self.prompt_queue.get_current_queue() + queue_info['queue_running'] = current_queue[0] + queue_info['queue_pending'] = current_queue[1] + return web.json_response(queue_info) + + @routes.post("/prompt") + async def post_prompt(request): + print("got prompt") + resp_code = 200 + out_string = "" + json_data = await request.json() + + if "number" in json_data: + number = float(json_data['number']) + else: + number = self.number + if "front" in json_data: + if json_data['front']: + number = -number + + self.number += 1 + + if "prompt" in json_data: + prompt = json_data["prompt"] + valid = main.validate_prompt(prompt) + extra_data = {} + if "extra_data" in json_data: + extra_data = json_data["extra_data"] + + if "client_id" in json_data: + extra_data["client_id"] = json_data["client_id"] + if valid[0]: + self.prompt_queue.put((number, id(prompt), prompt, extra_data)) + else: + resp_code = 400 + out_string = valid[1] + print("invalid prompt:", valid[1]) + + return web.Response(body=out_string, status=resp_code) + + @routes.post("/queue") + async def post_queue(request): + json_data = await request.json() + if "clear" in json_data: + if json_data["clear"]: + self.prompt_queue.wipe_queue() + if "delete" in json_data: + to_delete = json_data['delete'] + for id_to_delete in to_delete: + delete_func = lambda a: a[1] == int(id_to_delete) + self.prompt_queue.delete_queue_item(delete_func) + + return web.Response(status=200) + + self.app.add_routes(routes) + self.app.add_routes([ + web.static('/', self.web_root), + ]) + + def get_queue_info(self): + prompt_info = {} + exec_info = {} + exec_info['queue_remaining'] = self.prompt_queue.get_tasks_remaining() + prompt_info['exec_info'] = exec_info + return prompt_info + + async def send(self, event, data, sid=None): + await self.sio.emit(event, data, to=sid) + + def send_sync(self, event, data, sid=None): + self.loop.call_soon_threadsafe( + self.messages.put_nowait, (event, data, sid)) + + def queue_updated(self): + self.send_sync("status", self.get_queue_info()) + + async def publish_loop(self): + while True: + msg = await self.messages.get() + await self.send(*msg) + + async def start(self, address, port): + runner = web.AppRunner(self.app) + await runner.setup() + site = web.TCPSite(runner, address, port) + await site.start() + + if address == '': + address = '0.0.0.0' + print("Starting server\n") + print("To see the GUI go to: http://{}:{}".format(address, port)) \ No newline at end of file diff --git a/webshit/index.html b/webshit/index.html index 4f26f557..981a03f3 100644 --- a/webshit/index.html +++ b/webshit/index.html @@ -2,6 +2,7 @@ + - - + Queue size: X

@@ -801,6 +898,7 @@ function clearQueue() {


+
diff --git a/webshit/socket.io.min.js b/webshit/socket.io.min.js new file mode 100644 index 00000000..eb36c50e --- /dev/null +++ b/webshit/socket.io.min.js @@ -0,0 +1,7 @@ +/*! + * Socket.IO v4.6.0 + * (c) 2014-2023 Guillermo Rauch + * Released under the MIT License. + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).io=e()}(this,(function(){"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){for(var n=0;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,a=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return s=t.done,t},e:function(t){a=!0,o=t},f:function(){try{s||null==n.return||n.return()}finally{if(a)throw o}}}}var m=Object.create(null);m.open="0",m.close="1",m.ping="2",m.pong="3",m.message="4",m.upgrade="5",m.noop="6";var k=Object.create(null);Object.keys(m).forEach((function(t){k[m[t]]=t}));for(var b={type:"error",data:"parser error"},w="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),_="function"==typeof ArrayBuffer,E=function(t,e,n){var r,i=t.type,o=t.data;return w&&o instanceof Blob?e?n(o):O(o,n):_&&(o instanceof ArrayBuffer||(r=o,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(r):r&&r.buffer instanceof ArrayBuffer))?e?n(o):O(new Blob([o]),n):n(m[i]+(o||""))},O=function(t,e){var n=new FileReader;return n.onload=function(){var t=n.result.split(",")[1];e("b"+t)},n.readAsDataURL(t)},A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",R="undefined"==typeof Uint8Array?[]:new Uint8Array(256),T=0;T1?{type:k[n],data:t.substring(1)}:{type:k[n]}:b},S=function(t,e){if(C){var n=function(t){var e,n,r,i,o,s=.75*t.length,a=t.length,c=0;"="===t[t.length-1]&&(s--,"="===t[t.length-2]&&s--);var u=new ArrayBuffer(s),h=new Uint8Array(u);for(e=0;e>4,h[c++]=(15&r)<<4|i>>2,h[c++]=(3&i)<<6|63&o;return u}(t);return N(n,e)}return{base64:!0,data:t}},N=function(t,e){return"blob"===e&&t instanceof ArrayBuffer?new Blob([t]):t},x=String.fromCharCode(30);function L(t){if(t)return function(t){for(var e in L.prototype)t[e]=L.prototype[e];return t}(t)}L.prototype.on=L.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},L.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},L.prototype.off=L.prototype.removeListener=L.prototype.removeAllListeners=L.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n,r=this._callbacks["$"+t];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var i=0;i1?e-1:0),r=1;r0);return e}function W(){var t=z(+new Date);return t!==F?(K=0,F=t):t+"."+z(K++)}for(;Y<64;Y++)H[V[Y]]=Y;function $(t){var e="";for(var n in t)t.hasOwnProperty(n)&&(e.length&&(e+="&"),e+=encodeURIComponent(n)+"="+encodeURIComponent(t[n]));return e}function J(t){for(var e={},n=t.split("&"),r=0,i=n.length;r0&&void 0!==arguments[0]?arguments[0]:{};return i(t,{xd:this.xd,xs:this.xs},this.opts),new nt(this.uri(),t)}},{key:"doWrite",value:function(t,e){var n=this,r=this.request({method:"POST",data:t});r.on("success",e),r.on("error",(function(t,e){n.onError("xhr post error",t,e)}))}},{key:"doPoll",value:function(){var t=this,e=this.request();e.on("data",this.onData.bind(this)),e.on("error",(function(e,n){t.onError("xhr poll error",e,n)})),this.pollXhr=e}}]),s}(U),nt=function(t){o(i,t);var n=p(i);function i(t,r){var o;return e(this,i),D(f(o=n.call(this)),r),o.opts=r,o.method=r.method||"GET",o.uri=t,o.async=!1!==r.async,o.data=void 0!==r.data?r.data:null,o.create(),o}return r(i,[{key:"create",value:function(){var t=this,e=j(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");e.xdomain=!!this.opts.xd,e.xscheme=!!this.opts.xs;var n=this.xhr=new G(e);try{n.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders)for(var r in n.setDisableHeaderCheck&&n.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(r)&&n.setRequestHeader(r,this.opts.extraHeaders[r])}catch(t){}if("POST"===this.method)try{n.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(t){}try{n.setRequestHeader("Accept","*/*")}catch(t){}"withCredentials"in n&&(n.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(n.timeout=this.opts.requestTimeout),n.onreadystatechange=function(){4===n.readyState&&(200===n.status||1223===n.status?t.onLoad():t.setTimeoutFn((function(){t.onError("number"==typeof n.status?n.status:0)}),0))},n.send(this.data)}catch(e){return void this.setTimeoutFn((function(){t.onError(e)}),0)}"undefined"!=typeof document&&(this.index=i.requestsCount++,i.requests[this.index]=this)}},{key:"onError",value:function(t){this.emitReserved("error",t,this.xhr),this.cleanup(!0)}},{key:"cleanup",value:function(t){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=Z,t)try{this.xhr.abort()}catch(t){}"undefined"!=typeof document&&delete i.requests[this.index],this.xhr=null}}},{key:"onLoad",value:function(){var t=this.xhr.responseText;null!==t&&(this.emitReserved("data",t),this.emitReserved("success"),this.cleanup())}},{key:"abort",value:function(){this.cleanup()}}]),i}(L);if(nt.requestsCount=0,nt.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",rt);else if("function"==typeof addEventListener){addEventListener("onpagehide"in P?"pagehide":"unload",rt,!1)}function rt(){for(var t in nt.requests)nt.requests.hasOwnProperty(t)&&nt.requests[t].abort()}var it="function"==typeof Promise&&"function"==typeof Promise.resolve?function(t){return Promise.resolve().then(t)}:function(t,e){return e(t,0)},ot=P.WebSocket||P.MozWebSocket,st="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase(),at=function(t){o(i,t);var n=p(i);function i(t){var r;return e(this,i),(r=n.call(this,t)).supportsBinary=!t.forceBase64,r}return r(i,[{key:"name",get:function(){return"websocket"}},{key:"doOpen",value:function(){if(this.check()){var t=this.uri(),e=this.opts.protocols,n=st?{}:j(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(n.headers=this.opts.extraHeaders);try{this.ws=st?new ot(t,e,n):e?new ot(t,e):new ot(t)}catch(t){return this.emitReserved("error",t)}this.ws.binaryType=this.socket.binaryType||"arraybuffer",this.addEventListeners()}}},{key:"addEventListeners",value:function(){var t=this;this.ws.onopen=function(){t.opts.autoUnref&&t.ws._socket.unref(),t.onOpen()},this.ws.onclose=function(e){return t.onClose({description:"websocket connection closed",context:e})},this.ws.onmessage=function(e){return t.onData(e.data)},this.ws.onerror=function(e){return t.onError("websocket error",e)}}},{key:"write",value:function(t){var e=this;this.writable=!1;for(var n=function(n){var r=t[n],i=n===t.length-1;E(r,e.supportsBinary,(function(t){try{e.ws.send(t)}catch(t){}i&&it((function(){e.writable=!0,e.emitReserved("drain")}),e.setTimeoutFn)}))},r=0;r1&&void 0!==arguments[1]?arguments[1]:{};return e(this,a),(r=s.call(this)).writeBuffer=[],n&&"object"===t(n)&&(o=n,n=null),n?(n=ft(n),o.hostname=n.host,o.secure="https"===n.protocol||"wss"===n.protocol,o.port=n.port,n.query&&(o.query=n.query)):o.host&&(o.hostname=ft(o.host).host),D(f(r),o),r.secure=null!=o.secure?o.secure:"undefined"!=typeof location&&"https:"===location.protocol,o.hostname&&!o.port&&(o.port=r.secure?"443":"80"),r.hostname=o.hostname||("undefined"!=typeof location?location.hostname:"localhost"),r.port=o.port||("undefined"!=typeof location&&location.port?location.port:r.secure?"443":"80"),r.transports=o.transports||["polling","websocket"],r.writeBuffer=[],r.prevBufferLen=0,r.opts=i({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},o),r.opts.path=r.opts.path.replace(/\/$/,"")+(r.opts.addTrailingSlash?"/":""),"string"==typeof r.opts.query&&(r.opts.query=J(r.opts.query)),r.id=null,r.upgrades=null,r.pingInterval=null,r.pingTimeout=null,r.pingTimeoutTimer=null,"function"==typeof addEventListener&&(r.opts.closeOnBeforeunload&&(r.beforeunloadEventListener=function(){r.transport&&(r.transport.removeAllListeners(),r.transport.close())},addEventListener("beforeunload",r.beforeunloadEventListener,!1)),"localhost"!==r.hostname&&(r.offlineEventListener=function(){r.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",r.offlineEventListener,!1))),r.open(),r}return r(a,[{key:"createTransport",value:function(t){var e=i({},this.opts.query);e.EIO=4,e.transport=t,this.id&&(e.sid=this.id);var n=i({},this.opts.transportOptions[t],this.opts,{query:e,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new ct[t](n)}},{key:"open",value:function(){var t,e=this;if(this.opts.rememberUpgrade&&a.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))t="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((function(){e.emitReserved("error","No transports available")}),0);t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)}},{key:"setTransport",value:function(t){var e=this;this.transport&&this.transport.removeAllListeners(),this.transport=t,t.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(function(t){return e.onClose("transport close",t)}))}},{key:"probe",value:function(t){var e=this,n=this.createTransport(t),r=!1;a.priorWebsocketSuccess=!1;var i=function(){r||(n.send([{type:"ping",data:"probe"}]),n.once("packet",(function(t){if(!r)if("pong"===t.type&&"probe"===t.data){if(e.upgrading=!0,e.emitReserved("upgrading",n),!n)return;a.priorWebsocketSuccess="websocket"===n.name,e.transport.pause((function(){r||"closed"!==e.readyState&&(f(),e.setTransport(n),n.send([{type:"upgrade"}]),e.emitReserved("upgrade",n),n=null,e.upgrading=!1,e.flush())}))}else{var i=new Error("probe error");i.transport=n.name,e.emitReserved("upgradeError",i)}})))};function o(){r||(r=!0,f(),n.close(),n=null)}var s=function(t){var r=new Error("probe error: "+t);r.transport=n.name,o(),e.emitReserved("upgradeError",r)};function c(){s("transport closed")}function u(){s("socket closed")}function h(t){n&&t.name!==n.name&&o()}var f=function(){n.removeListener("open",i),n.removeListener("error",s),n.removeListener("close",c),e.off("close",u),e.off("upgrading",h)};n.once("open",i),n.once("error",s),n.once("close",c),this.once("close",u),this.once("upgrading",h),n.open()}},{key:"onOpen",value:function(){if(this.readyState="open",a.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade)for(var t=0,e=this.upgrades.length;t1))return this.writeBuffer;for(var t,e=1,n=0;n=57344?n+=3:(r++,n+=4);return n}(t):Math.ceil(1.33*(t.byteLength||t.size))),n>0&&e>this.maxPayload)return this.writeBuffer.slice(0,n);e+=2}return this.writeBuffer}},{key:"write",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"send",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"sendPacket",value:function(t,e,n,r){if("function"==typeof e&&(r=e,e=void 0),"function"==typeof n&&(r=n,n=null),"closing"!==this.readyState&&"closed"!==this.readyState){(n=n||{}).compress=!1!==n.compress;var i={type:t,data:e,options:n};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}}},{key:"close",value:function(){var t=this,e=function(){t.onClose("forced close"),t.transport.close()},n=function n(){t.off("upgrade",n),t.off("upgradeError",n),e()},r=function(){t.once("upgrade",n),t.once("upgradeError",n)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(function(){t.upgrading?r():e()})):this.upgrading?r():e()),this}},{key:"onError",value:function(t){a.priorWebsocketSuccess=!1,this.emitReserved("error",t),this.onClose("transport error",t)}},{key:"onClose",value:function(t,e){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&(removeEventListener("beforeunload",this.beforeunloadEventListener,!1),removeEventListener("offline",this.offlineEventListener,!1)),this.readyState="closed",this.id=null,this.emitReserved("close",t,e),this.writeBuffer=[],this.prevBufferLen=0)}},{key:"filterUpgrades",value:function(t){for(var e=[],n=0,r=t.length;n=0&&e.num0;case Et.ACK:case Et.BINARY_ACK:return Array.isArray(n)}}}]),a}(L),Rt=function(){function t(n){e(this,t),this.packet=n,this.buffers=[],this.reconPack=n}return r(t,[{key:"takeBinaryData",value:function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=wt(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}},{key:"finishedReconstruction",value:function(){this.reconPack=null,this.buffers=[]}}]),t}(),Tt=Object.freeze({__proto__:null,protocol:5,get PacketType(){return Et},Encoder:Ot,Decoder:At});function Ct(t,e,n){return t.on(e,n),function(){t.off(e,n)}}var Bt=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1}),St=function(t){o(a,t);var n=p(a);function a(t,r,o){var s;return e(this,a),(s=n.call(this)).connected=!1,s.recovered=!1,s.receiveBuffer=[],s.sendBuffer=[],s._queue=[],s.ids=0,s.acks={},s.flags={},s.io=t,s.nsp=r,o&&o.auth&&(s.auth=o.auth),s._opts=i({},o),s.io._autoConnect&&s.open(),s}return r(a,[{key:"disconnected",get:function(){return!this.connected}},{key:"subEvents",value:function(){if(!this.subs){var t=this.io;this.subs=[Ct(t,"open",this.onopen.bind(this)),Ct(t,"packet",this.onpacket.bind(this)),Ct(t,"error",this.onerror.bind(this)),Ct(t,"close",this.onclose.bind(this))]}}},{key:"active",get:function(){return!!this.subs}},{key:"connect",value:function(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}},{key:"open",value:function(){return this.connect()}},{key:"send",value:function(){for(var t=arguments.length,e=new Array(t),n=0;n1?e-1:0),r=1;r1?n-1:0),i=1;in._opts.retries&&(n._queue.shift(),e&&e(t));else if(n._queue.shift(),e){for(var o=arguments.length,s=new Array(o>1?o-1:0),a=1;a0&&t.jitter<=1?t.jitter:0,this.attempts=0}Nt.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},Nt.prototype.reset=function(){this.attempts=0},Nt.prototype.setMin=function(t){this.ms=t},Nt.prototype.setMax=function(t){this.max=t},Nt.prototype.setJitter=function(t){this.jitter=t};var xt=function(n){o(s,n);var i=p(s);function s(n,r){var o,a;e(this,s),(o=i.call(this)).nsps={},o.subs=[],n&&"object"===t(n)&&(r=n,n=void 0),(r=r||{}).path=r.path||"/socket.io",o.opts=r,D(f(o),r),o.reconnection(!1!==r.reconnection),o.reconnectionAttempts(r.reconnectionAttempts||1/0),o.reconnectionDelay(r.reconnectionDelay||1e3),o.reconnectionDelayMax(r.reconnectionDelayMax||5e3),o.randomizationFactor(null!==(a=r.randomizationFactor)&&void 0!==a?a:.5),o.backoff=new Nt({min:o.reconnectionDelay(),max:o.reconnectionDelayMax(),jitter:o.randomizationFactor()}),o.timeout(null==r.timeout?2e4:r.timeout),o._readyState="closed",o.uri=n;var c=r.parser||Tt;return o.encoder=new c.Encoder,o.decoder=new c.Decoder,o._autoConnect=!1!==r.autoConnect,o._autoConnect&&o.open(),o}return r(s,[{key:"reconnection",value:function(t){return arguments.length?(this._reconnection=!!t,this):this._reconnection}},{key:"reconnectionAttempts",value:function(t){return void 0===t?this._reconnectionAttempts:(this._reconnectionAttempts=t,this)}},{key:"reconnectionDelay",value:function(t){var e;return void 0===t?this._reconnectionDelay:(this._reconnectionDelay=t,null===(e=this.backoff)||void 0===e||e.setMin(t),this)}},{key:"randomizationFactor",value:function(t){var e;return void 0===t?this._randomizationFactor:(this._randomizationFactor=t,null===(e=this.backoff)||void 0===e||e.setJitter(t),this)}},{key:"reconnectionDelayMax",value:function(t){var e;return void 0===t?this._reconnectionDelayMax:(this._reconnectionDelayMax=t,null===(e=this.backoff)||void 0===e||e.setMax(t),this)}},{key:"timeout",value:function(t){return arguments.length?(this._timeout=t,this):this._timeout}},{key:"maybeReconnectOnOpen",value:function(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}},{key:"open",value:function(t){var e=this;if(~this._readyState.indexOf("open"))return this;this.engine=new lt(this.uri,this.opts);var n=this.engine,r=this;this._readyState="opening",this.skipReconnect=!1;var i=Ct(n,"open",(function(){r.onopen(),t&&t()})),o=Ct(n,"error",(function(n){r.cleanup(),r._readyState="closed",e.emitReserved("error",n),t?t(n):r.maybeReconnectOnOpen()}));if(!1!==this._timeout){var s=this._timeout;0===s&&i();var a=this.setTimeoutFn((function(){i(),n.close(),n.emit("error",new Error("timeout"))}),s);this.opts.autoUnref&&a.unref(),this.subs.push((function(){clearTimeout(a)}))}return this.subs.push(i),this.subs.push(o),this}},{key:"connect",value:function(t){return this.open(t)}},{key:"onopen",value:function(){this.cleanup(),this._readyState="open",this.emitReserved("open");var t=this.engine;this.subs.push(Ct(t,"ping",this.onping.bind(this)),Ct(t,"data",this.ondata.bind(this)),Ct(t,"error",this.onerror.bind(this)),Ct(t,"close",this.onclose.bind(this)),Ct(this.decoder,"decoded",this.ondecoded.bind(this)))}},{key:"onping",value:function(){this.emitReserved("ping")}},{key:"ondata",value:function(t){try{this.decoder.add(t)}catch(t){this.onclose("parse error",t)}}},{key:"ondecoded",value:function(t){var e=this;it((function(){e.emitReserved("packet",t)}),this.setTimeoutFn)}},{key:"onerror",value:function(t){this.emitReserved("error",t)}},{key:"socket",value:function(t,e){var n=this.nsps[t];return n||(n=new St(this,t,e),this.nsps[t]=n),this._autoConnect&&n.connect(),n}},{key:"_destroy",value:function(t){for(var e=0,n=Object.keys(this.nsps);e=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{var n=this.backoff.duration();this._reconnecting=!0;var r=this.setTimeoutFn((function(){e.skipReconnect||(t.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((function(n){n?(e._reconnecting=!1,e.reconnect(),t.emitReserved("reconnect_error",n)):e.onreconnect()})))}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){clearTimeout(r)}))}}},{key:"onreconnect",value:function(){var t=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",t)}}]),s}(L),Lt={};function Pt(e,n){"object"===t(e)&&(n=e,e=void 0);var r,i=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,r=t;n=n||"undefined"!=typeof location&&location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==n?n.protocol+"//"+t:"https://"+t),r=ft(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+e,r.href=r.protocol+"://"+i+(n&&n.port===r.port?"":":"+r.port),r}(e,(n=n||{}).path||"/socket.io"),o=i.source,s=i.id,a=i.path,c=Lt[s]&&a in Lt[s].nsps;return n.forceNew||n["force new connection"]||!1===n.multiplex||c?r=new xt(o,n):(Lt[s]||(Lt[s]=new xt(o,n)),r=Lt[s]),i.query&&!n.query&&(n.query=i.queryKey),r.socket(i.path,n)}return i(Pt,{Manager:xt,Socket:St,io:Pt,connect:Pt}),Pt})); +//# sourceMappingURL=socket.io.min.js.map \ No newline at end of file