diff --git a/app/data/cfg.json b/app/data/cfg.json index de49d0d..4c40dac 100644 --- a/app/data/cfg.json +++ b/app/data/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.7.7", + "version": "1.7.18", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/app/display.html b/app/display.html index 32c8115..512d99b 100644 --- a/app/display.html +++ b/app/display.html @@ -13,6 +13,16 @@ body.meter { background: rgb(117, 117, 117); } + body.meter #img, + body.meter #can { + display: none; + } + body.image #can{ + display: none; + } + body.image #img { + display: block; + } #img { position: absolute; /*background-image: url(".../img/background.jpg");*/ @@ -46,16 +56,16 @@ + + \ No newline at end of file diff --git a/app/src/lib/client/index.ts b/app/src/lib/client/index.ts new file mode 100644 index 0000000..f3dda9b --- /dev/null +++ b/app/src/lib/client/index.ts @@ -0,0 +1,271 @@ +/*! NoSleep.min.js v0.12.0 - git.io/vfn01 - Rich Tibbett - MIT license */ +//@ts-ignore +!function(A,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.NoSleep=e():A.NoSleep=e()}(this,(function(){return function(A){var e={};function B(g){if(e[g])return e[g].exports;var o=e[g]={i:g,l:!1,exports:{}};return A[g].call(o.exports,o,o.exports,B),o.l=!0,o.exports}return B.m=A,B.c=e,B.d=function(A,e,g){B.o(A,e)||Object.defineProperty(A,e,{enumerable:!0,get:g})},B.r=function(A){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(A,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(A,"__esModule",{value:!0})},B.t=function(A,e){if(1&e&&(A=B(A)),8&e)return A;if(4&e&&"object"==typeof A&&A&&A.__esModule)return A;var g=Object.create(null);if(B.r(g),Object.defineProperty(g,"default",{enumerable:!0,value:A}),2&e&&"string"!=typeof A)for(var o in A)B.d(g,o,function(e){return A[e]}.bind(null,o));return g},B.n=function(A){var e=A&&A.__esModule?function(){return A.default}:function(){return A};return B.d(e,"a",e),e},B.o=function(A,e){return Object.prototype.hasOwnProperty.call(A,e)},B.p="",B(B.s=0)}([function(A,e,B){"use strict";var g=function(){function A(A,e){for(var B=0;B.5&&(e.noSleepVideo.currentTime=Math.random())}))})))}return g(A,[{key:"_addSourceToVideo",value:function(A,e,B){var g=document.createElement("source");g.src=B,g.type="video/"+e,A.appendChild(g)}},{key:"enable",value:function(){var A=this;return Q()?navigator.wakeLock.request("screen").then((function(e){A._wakeLock=e,A.enabled=!0,console.log("Wake Lock active."),A._wakeLock.addEventListener("release",(function(){console.log("Wake Lock released.")}))})).catch((function(e){throw A.enabled=!1,console.error(e.name+", "+e.message),e})):C()?(this.disable(),console.warn("\n NoSleep enabled for older iOS devices. This can interrupt\n active or long-running network requests from completing successfully.\n See https://github.com/richtr/NoSleep.js/issues/15 for more details.\n "),this.noSleepTimer=window.setInterval((function(){document.hidden||(window.location.href=window.location.href.split("#")[0],window.setTimeout(window.stop,0))}),15e3),this.enabled=!0,Promise.resolve()):this.noSleepVideo.play().then((function(e){return A.enabled=!0,e})).catch((function(e){throw A.enabled=!1,e}))}},{key:"disable",value:function(){Q()?(this._wakeLock&&this._wakeLock.release(),this._wakeLock=null):C()?this.noSleepTimer&&(console.warn("\n NoSleep now disabled for older iOS devices.\n "),window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):this.noSleepVideo.pause(),this.enabled=!1}},{key:"isEnabled",get:function(){return this.enabled}}]),A}();A.exports=i},function(A,e,B){"use strict";A.exports={webm:"data:video/webm;base64,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",mp4:"data:video/mp4;base64,AAAAHGZ0eXBNNFYgAAACAGlzb21pc28yYXZjMQAAAAhmcmVlAAAGF21kYXTeBAAAbGliZmFhYyAxLjI4AABCAJMgBDIARwAAArEGBf//rdxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNDIgcjIgOTU2YzhkOCAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMTQgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0wIHJlZj0zIGRlYmxvY2s9MTowOjAgYW5hbHlzZT0weDE6MHgxMTEgbWU9aGV4IHN1Ym1lPTcgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MSBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTEgOHg4ZGN0PTAgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz02IGxvb2thaGVhZF90aHJlYWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCB2YnZfbWF4cmF0ZT03NjggdmJ2X2J1ZnNpemU9MzAwMCBjcmZfbWF4PTAuMCBuYWxfaHJkPW5vbmUgZmlsbGVyPTAgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAFZliIQL8mKAAKvMnJycnJycnJycnXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXiEASZACGQAjgCEASZACGQAjgAAAAAdBmjgX4GSAIQBJkAIZACOAAAAAB0GaVAX4GSAhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGagC/AySEASZACGQAjgAAAAAZBmqAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZrAL8DJIQBJkAIZACOAAAAABkGa4C/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmwAvwMkhAEmQAhkAI4AAAAAGQZsgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGbQC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm2AvwMkhAEmQAhkAI4AAAAAGQZuAL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGboC/AySEASZACGQAjgAAAAAZBm8AvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZvgL8DJIQBJkAIZACOAAAAABkGaAC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmiAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZpAL8DJIQBJkAIZACOAAAAABkGaYC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBmoAvwMkhAEmQAhkAI4AAAAAGQZqgL8DJIQBJkAIZACOAIQBJkAIZACOAAAAABkGawC/AySEASZACGQAjgAAAAAZBmuAvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZsAL8DJIQBJkAIZACOAAAAABkGbIC/AySEASZACGQAjgCEASZACGQAjgAAAAAZBm0AvwMkhAEmQAhkAI4AhAEmQAhkAI4AAAAAGQZtgL8DJIQBJkAIZACOAAAAABkGbgCvAySEASZACGQAjgCEASZACGQAjgAAAAAZBm6AnwMkhAEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AhAEmQAhkAI4AAAAhubW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAABDcAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAzB0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAA+kAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAALAAAACQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAPpAAAAAAABAAAAAAKobWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAB1MAAAdU5VxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAACU21pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAhNzdGJsAAAAr3N0c2QAAAAAAAAAAQAAAJ9hdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAALAAkABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAALWF2Y0MBQsAN/+EAFWdCwA3ZAsTsBEAAAPpAADqYA8UKkgEABWjLg8sgAAAAHHV1aWRraEDyXyRPxbo5pRvPAyPzAAAAAAAAABhzdHRzAAAAAAAAAAEAAAAeAAAD6QAAABRzdHNzAAAAAAAAAAEAAAABAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAAIxzdHN6AAAAAAAAAAAAAAAeAAADDwAAAAsAAAALAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAiHN0Y28AAAAAAAAAHgAAAEYAAANnAAADewAAA5gAAAO0AAADxwAAA+MAAAP2AAAEEgAABCUAAARBAAAEXQAABHAAAASMAAAEnwAABLsAAATOAAAE6gAABQYAAAUZAAAFNQAABUgAAAVkAAAFdwAABZMAAAWmAAAFwgAABd4AAAXxAAAGDQAABGh0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAACAAAAAAAABDcAAAAAAAAAAAAAAAEBAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAQkAAADcAABAAAAAAPgbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAC7gAAAykBVxAAAAAAALWhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAABTb3VuZEhhbmRsZXIAAAADi21pbmYAAAAQc21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAADT3N0YmwAAABnc3RzZAAAAAAAAAABAAAAV21wNGEAAAAAAAAAAQAAAAAAAAAAAAIAEAAAAAC7gAAAAAAAM2VzZHMAAAAAA4CAgCIAAgAEgICAFEAVBbjYAAu4AAAADcoFgICAAhGQBoCAgAECAAAAIHN0dHMAAAAAAAAAAgAAADIAAAQAAAAAAQAAAkAAAAFUc3RzYwAAAAAAAAAbAAAAAQAAAAEAAAABAAAAAgAAAAIAAAABAAAAAwAAAAEAAAABAAAABAAAAAIAAAABAAAABgAAAAEAAAABAAAABwAAAAIAAAABAAAACAAAAAEAAAABAAAACQAAAAIAAAABAAAACgAAAAEAAAABAAAACwAAAAIAAAABAAAADQAAAAEAAAABAAAADgAAAAIAAAABAAAADwAAAAEAAAABAAAAEAAAAAIAAAABAAAAEQAAAAEAAAABAAAAEgAAAAIAAAABAAAAFAAAAAEAAAABAAAAFQAAAAIAAAABAAAAFgAAAAEAAAABAAAAFwAAAAIAAAABAAAAGAAAAAEAAAABAAAAGQAAAAIAAAABAAAAGgAAAAEAAAABAAAAGwAAAAIAAAABAAAAHQAAAAEAAAABAAAAHgAAAAIAAAABAAAAHwAAAAQAAAABAAAA4HN0c3oAAAAAAAAAAAAAADMAAAAaAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAACMc3RjbwAAAAAAAAAfAAAALAAAA1UAAANyAAADhgAAA6IAAAO+AAAD0QAAA+0AAAQAAAAEHAAABC8AAARLAAAEZwAABHoAAASWAAAEqQAABMUAAATYAAAE9AAABRAAAAUjAAAFPwAABVIAAAVuAAAFgQAABZ0AAAWwAAAFzAAABegAAAX7AAAGFwAAAGJ1ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWlsc3QAAAAlqXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNTUuMzMuMTAw"}}])})); + +interface McopyRequest { + action : string; + id : string; + ratio? : number; + image? : string; +} + +interface McopyResponse { + action : string; + id : string; +} + +const ws = new WebSocket(`ws://${location.host.split(':')[0]}:{{PORT}}/`); +let imgTmp : HTMLImageElement + +function preventSleep () { + //@ts-ignore + var noSleep = new NoSleep(); + document.addEventListener('click', function enableNoSleep() { + document.removeEventListener('click', enableNoSleep, false); + noSleep.enable(); + document.getElementById('nosleep').remove(); + }, false); +} + +function fullScreen () { + document.addEventListener('click', function enableFullScreen() { + const elem = document.querySelector('body'); + document.removeEventListener('click', enableFullScreen, false); + if (elem.requestFullscreen) { + elem.requestFullscreen(); + //@ts-ignore + } else if (typeof elem.webkitRequestFullscreen !== 'undefined') { /* Safari */ + //@ts-ignore + elem.webkitRequestFullscreen(); + //@ts-ignore + } else if (typeof elem.msRequestFullscreen !== 'undefined') { /* IE11 */ + //@ts-ignore + elem.msRequestFullscreen(); + } + }, false); +} + +function displayMode () { + fullScreen(); + preventSleep(); +} + +async function setImage (src : string) { + return new Promise(async (resolve : Function, reject : Function) => { + imgTmp = new Image() + let img = document.getElementById('img') + let body = document.querySelector('body') + body.className = '' + body.classList.add('image') + imgTmp.onload = function () { + img.style.backgroundImage = `url('${src}')`; + return resolve(src) + }; + imgTmp.src = src + }); +} + +async function onImage (arg: McopyRequest) { + return setImage(arg.image) +} + +async function onMeter (arg : McopyRequest ) { + console.log('meter') + const body = document.querySelector('body') + body.className = '' + body.classList.add('meter') +} + +async function onBlank (arg : McopyRequest ) { + console.log('blank') + const body = document.querySelector('body') + body.className = '' + body.classList.add('blank') +} + +async function onFocus (arg : McopyRequest ) { + console.log('focus') + const can : HTMLCanvasElement = document.getElementById('can') as HTMLCanvasElement + const dpr = window.devicePixelRatio || 1 + let ctx; + const body = document.querySelector('body') + body.className = '' + + if (!can.classList.contains('show')) { + can.classList.add('show') + } + + can.width = window.innerWidth * dpr + can.height = window.innerHeight * dpr + + can.style.width = `${window.innerWidth}px` + can.style.height = `${window.innerHeight}px` + + ctx = can.getContext('2d') + ctx.scale(dpr, dpr) + + try{ + await drawFocus(can, ctx) + } catch (err) { + alert(JSON.stringify(err)) + } +} + +async function drawFocus (can : HTMLCanvasElement, ctx : CanvasRenderingContext2D) { + const count = 20 + const half = Math.round(count / 2) + const dpr = window.devicePixelRatio || 1 + const w = can.width / dpr + const h = can.height / dpr + const longest = w >= h ? w * 1.5 : h * 1.5 + const opp = Math.tan(360 / (count * 32)) * longest / 10 + + for (let i = 0; i < count; i++) { + ctx.beginPath() + ctx.moveTo(w / 2, h / 2) + ctx.lineTo((w / 2) + opp, longest) + ctx.lineTo((w / 2) - opp, longest) + ctx.fill() + ctx.translate(w / 2, h / 2); + ctx.rotate((360 / count) * Math.PI / 180) + ctx.translate(- w / 2, -h / 2) + } +} + +async function onField (arg : McopyRequest) { + console.log('field guide') + const can : HTMLCanvasElement = document.getElementById('can') as HTMLCanvasElement + const dpr : number = window.devicePixelRatio || 1 + const screen : number = window.outerWidth / window.outerHeight + const body = document.querySelector('body') + body.className = '' + let ctx : CanvasRenderingContext2D; + if (!can.classList.contains('show')) { + can.classList.add('show') + } + if (arg.ratio) { + if (arg.ratio < screen) { + can.width = (window.innerHeight * arg.ratio) * dpr + can.height = window.innerHeight * dpr + } else { + can.width =window.innerWidth * dpr + can.height = (window.innerWidth / arg.ratio) * dpr + } + } else { + can.width = window.innerWidth * dpr + can.height = window.innerHeight * dpr + } + + if (arg.ratio) { + if (arg.ratio < screen) { + can.style.width = `${window.innerHeight * arg.ratio}px` + can.style.height = `${window.innerHeight}px` + } else { + can.style.width = `${window.innerWidth}px` + can.style.height = `${window.innerWidth / arg.ratio}px` + } + } else { + can.style.width = `${window.innerWidth}px` + can.style.height = `${window.innerHeight}px` + } + + ctx = can.getContext('2d') + ctx.scale(dpr, dpr) + + try{ + await drawField(can, ctx) + } catch (err) { + alert(JSON.stringify(err)) + } +} +// draw a field guide +async function drawField (can : HTMLCanvasElement, ctx : CanvasRenderingContext2D) { + const count = 20 + const half = Math.round(count / 2) + const dpr = window.devicePixelRatio || 1 + const w = can.width / dpr + const h = can.height / dpr + const wsec = w / count + const hsec = h / count + const spacer = 12 + const fontSize = 18 + + ctx.lineWidth = 2 + + ctx.moveTo(w / 2, 0) + ctx.lineTo(w / 2, h) + ctx.stroke() + ctx.moveTo(0, h / 2) + ctx.lineTo(w, h / 2) + ctx.stroke() + + for (let i = 0; i < count; i++) { + ctx.moveTo(wsec * i, hsec * i) + ctx.lineTo(wsec * i, h - (hsec * i)) + ctx.stroke() + ctx.moveTo(wsec * i, hsec * i) + ctx.lineTo(w - (wsec * i), hsec * i) + ctx.stroke() + } + + ctx.lineWidth = 1 + + ctx.font = `${fontSize}px Arial` + for (let i = 0; i < half; i++) { + //left count + ctx.fillText(`${(half - i)}`, (wsec * i) + spacer, (h / 2) - spacer) + //right count + ctx.fillText(`${(half - i)}`, (w - (wsec * i)) - (spacer * 2), (h / 2) + (spacer * 2)) + //up count + ctx.fillText(`${(half - i)}`, (w / 2) + spacer, (hsec * i) + spacer + (fontSize / 2) ) + //down count + ctx.fillText(`${(half - i)}`, (w / 2) - (spacer * 2), (h - (hsec * i)) - spacer) + } +} + +//keep connection alive +async function onPing (arg : McopyRequest) { + //console.log('ping') + return true +} + +function send (obj : McopyResponse) { + if (ws) { + ws.send(JSON.stringify(obj)) + } +} + +let actions : any = { + mcopy : async (arg : McopyRequest) => { console.log('Connected'); return true; }, + image : onImage, + field : onField, + meter : onMeter, + focus : onFocus, + ping : onPing +} + +ws.onmessage = async (event : any) => { + const req : McopyRequest = JSON.parse(event.data); + let res : McopyResponse; + + if (typeof actions[req.action] !== 'undefined') { + try { + await actions[req.action](req) + } catch (err) { + console.error(err) + } + res = { + action : req.action, + id : req.id + } + send(res) + } +} + +ws.onclose = async (event : any) => { + console.log('Connection closed') +}; + +(function main () { + displayMode(); +})() \ No newline at end of file diff --git a/data/cfg.json b/data/cfg.json index de49d0d..4c40dac 100644 --- a/data/cfg.json +++ b/data/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.7.7", + "version": "1.7.18", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/package-lock.json b/package-lock.json index eb218f4..c3e3644 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcopy", - "version": "1.7.7", + "version": "1.7.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mcopy", - "version": "1.7.7", + "version": "1.7.18", "license": "MIT", "dependencies": { "arduino": "file:app/lib/arduino", @@ -28,12 +28,14 @@ }, "devDependencies": { "@types/electron": "^1.6.10", + "@types/express": "^4.17.17", "@types/fs-extra": "^9.0.7", "@types/jimp": "^0.2.28", "@types/node": "^14.14.31", "@types/request": "^2.48.5", "@types/sharp": "^0.27.1", "@types/uuid": "^8.3.0", + "@types/ws": "^8.5.4", "jsdoc-to-markdown": "^6.0.1", "typescript": "^4.1.5" } @@ -596,12 +598,31 @@ "node": ">=6" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/electron": { "version": "1.6.10", "resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz", @@ -611,6 +632,29 @@ "electron": "*" } }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/fs-extra": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz", @@ -629,12 +673,30 @@ "jimp": "*" } }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, "node_modules/@types/node": { "version": "14.14.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==", "dev": true }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, "node_modules/@types/request": { "version": "2.48.5", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", @@ -647,6 +709,16 @@ "form-data": "^2.5.0" } }, + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, "node_modules/@types/sharp": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.27.1.tgz", @@ -668,6 +740,15 @@ "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/ansi-escape-sequences": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", @@ -3284,12 +3365,31 @@ "defer-to-connect": "^1.0.1" } }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/electron": { "version": "1.6.10", "resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz", @@ -3299,6 +3399,29 @@ "electron": "*" } }, + "@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/fs-extra": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz", @@ -3317,12 +3440,30 @@ "jimp": "*" } }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, "@types/node": { "version": "14.14.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==", "dev": true }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, "@types/request": { "version": "2.48.5", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", @@ -3335,6 +3476,16 @@ "form-data": "^2.5.0" } }, + "@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, "@types/sharp": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.27.1.tgz", @@ -3356,6 +3507,15 @@ "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", "dev": true }, + "@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "ansi-escape-sequences": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", diff --git a/package.json b/package.json index e89fc14..ff58382 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcopy", - "version": "1.7.7", + "version": "1.7.18", "description": "Small gauge film optical printer platform", "main": "build.js", "directories": { @@ -26,12 +26,14 @@ "homepage": "https://github.com/sixteenmillimeter/mcopy#readme", "devDependencies": { "@types/electron": "^1.6.10", + "@types/express": "^4.17.17", "@types/fs-extra": "^9.0.7", "@types/jimp": "^0.2.28", "@types/node": "^14.14.31", "@types/request": "^2.48.5", "@types/sharp": "^0.27.1", "@types/uuid": "^8.3.0", + "@types/ws": "^8.5.4", "jsdoc-to-markdown": "^6.0.1", "typescript": "^4.1.5" }, @@ -47,8 +49,8 @@ "light": "file:app/lib/light", "log": "file:app/lib/log", "mscript": "file:app/lib/mscript", - "proj": "file:app/lib/proj", "processing": "file:app/lib/processing", + "proj": "file:app/lib/proj", "sequencer": "file:app/lib/sequencer", "settings": "file:app/lib/settings", "system": "file:app/lib/system" diff --git a/processing/mcopy/cfg.json b/processing/mcopy/cfg.json index de49d0d..4c40dac 100644 --- a/processing/mcopy/cfg.json +++ b/processing/mcopy/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.7.7", + "version": "1.7.18", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/scripts/version.sh b/scripts/version.sh index 25ac831..f09dd28 100644 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -8,7 +8,7 @@ echo "VERSION: $version" git add ./package.json -declare -a fileArr=("./package-lock.json" "./data/cfg.json" "./app/package.json" "./app/package-lock.json" "./processing/mcopy/cfg.json") +declare -a fileArr=("./package-lock.json" "./data/cfg.json" "./app/package.json" "./app/package-lock.json" "./app/data/cfg.json" "./processing/mcopy/cfg.json") for i in "${fileArr[@]}" do diff --git a/src/display/index.ts b/src/display/index.ts index c6dab4b..44e03b2 100644 --- a/src/display/index.ts +++ b/src/display/index.ts @@ -25,7 +25,8 @@ class WebView { webPreferences: { nodeIntegration: true, allowRunningInsecureContent: false, - enableRemoteModule: true + enableRemoteModule: true, + contextIsolation: false }, width: 800, height: 600, @@ -43,6 +44,7 @@ class WebView { prefs.y = display.y + 50; } this.digitalWindow = new BrowserWindow(prefs); + require('@electron/remote/main').enable(this.digitalWindow.webContents) this.digitalWindow.loadURL(pageUrl); if (process.argv.indexOf('-d') !== -1 || process.argv.indexOf('--dev') !== -1) { this.digitalWindow.webContents.openDevTools(); diff --git a/src/filmout/index.ts b/src/filmout/index.ts index 5917884..07a938b 100644 --- a/src/filmout/index.ts +++ b/src/filmout/index.ts @@ -37,6 +37,7 @@ class FilmOut { private ipc : any; private ui : any; private log : any; + private server : any; /** * @constructor * Builds FilmOut class with display, ffmpeg, ffprobe, ui and light as internal properties. @@ -47,8 +48,9 @@ class FilmOut { * @param {object} ui Electron ui object * @param {object} light Light device object **/ - constructor (display : any, ffmpeg : any, ffprobe : any, ui : any, light : any) { + constructor (display : any, server : any, ffmpeg : any, ffprobe : any, ui : any, light : any) { this.display = display; + this.server = server; this.ffmpeg = ffmpeg; this.ffprobe = ffprobe; this.ui = ui; @@ -133,6 +135,11 @@ class FilmOut { throw err; } + if (this.server.displayImage(path)) { + await delay(20) + return + } + await this.display.show(path); await delay(20); } @@ -430,6 +437,9 @@ class FilmOut { } try { + if (await this.server.displayImage(path)) { + return + } await this.display.open(); await this.display.show(path); } catch (err) { @@ -442,6 +452,9 @@ class FilmOut { async focus (evt : any, arg : any) { this.log.info(`Showing focus screen`); try { + if (await this.server.cmdAll('focus')) { + return + } await this.display.open(); await this.display.focus(); } catch (err) { @@ -455,8 +468,12 @@ class FilmOut { const ratio : number = arg.ratio; this.log.info(`Showing field guide screen`); try { + if (await this.server.cmdAll('field', { ratio })) { + return + } await this.display.open(); await this.display.field(ratio); + } catch (err) { this.log.error(err, 'FILMOUT', true, true); } @@ -467,6 +484,9 @@ class FilmOut { async meter (evt : any, arg : any) { this.log.info(`Showing meter screen`); try { + if (await this.server.cmdAll('meter')) { + return + } await this.display.open(); await this.display.meter(); } catch (err) { @@ -478,8 +498,12 @@ class FilmOut { **/ async close (evt : any, arg : any) { try { + if (await this.server.cmdAll('blank')) { + return + } await this.display.hide(); await this.display.close(); + } catch (err) { this.log.error(err, 'FILMOUT', true, true); } @@ -493,6 +517,6 @@ class FilmOut { } } -module.exports = (display : any, ffmpeg : any, ffprobe : any, ui : any, light : any) => { - return new FilmOut(display, ffmpeg, ffprobe, ui, light); +module.exports = (display : any, server : any, ffmpeg : any, ffprobe : any, ui : any, light : any) => { + return new FilmOut(display, server, ffmpeg, ffprobe, ui, light); } diff --git a/src/log/index.ts b/src/log/index.ts index 09ae03c..6152563 100644 --- a/src/log/index.ts +++ b/src/log/index.ts @@ -53,7 +53,7 @@ async function logFile () { * * @returns {object} Logger transport **/ -module.exports = async function (arg : any) { +module.exports = async function Log (arg : any) { let consoleFormat : any = { colorize : true } diff --git a/src/proj/index.ts b/src/proj/index.ts index 5b29ca2..641343a 100644 --- a/src/proj/index.ts +++ b/src/proj/index.ts @@ -155,7 +155,7 @@ class Projector { } message += ' 1 frame' } else if (cmd === this.cfg.arduino.cmd.projectors) { - message += 'Projectors both MOVED 1 frame each'; + message += 'Projectors both MOVED 1 frame each' } message += ` ${ms}ms` this.log.info(message, 'PROJECTOR') @@ -164,5 +164,5 @@ class Projector { } module.exports = function (arduino : Arduino, cfg : any, ui : any, filmout : any, second : boolean) { - return new Projector(arduino, cfg, ui, filmout, second); + return new Projector(arduino, cfg, ui, filmout, second) } \ No newline at end of file diff --git a/src/sequencer/index.ts b/src/sequencer/index.ts index c5fcf05..c42989c 100644 --- a/src/sequencer/index.ts +++ b/src/sequencer/index.ts @@ -168,7 +168,7 @@ class Sequencer { this.log.info(`Starting sequence...`); this.ui.send(this.id, { start : true }); - if (this.cmd.proj.filmout.state.enabled === true) { + if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) { await this.cmd.proj.filmout.display.open(); } @@ -209,7 +209,7 @@ class Sequencer { this.ui.send(this.id, { loop : x, stop : true }); } - if (this.cmd.proj.filmout.state.enabled === true) { + if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) { await this.cmd.proj.filmout.display.close(); } diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..e4f379e --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,244 @@ +import WebSocket, { WebSocketServer } from 'ws' +import express, { Express, Request, Response, Application } from 'express' +import { readFile } from 'fs/promises' +import { basename } from 'path' +import { v4 as uuidv4 } from 'uuid' +import Log = require('log') +import { delay } from 'delay' + +interface ServerData { + [key: string]: string; + PORT? : string; + SCRIPT? : string; +} + +interface ServerTemplate { + name : string; + path : string; + data? : string; +} + +interface ServerProxy { + path : string; +} + +interface ServerProxyList { + [key: string]: ServerProxy; +} + +interface ServerQueue { + [key: string]: Function; +} + +class Server { + private id : string = 'server' + public isActive : boolean = false + private log : any + private templates : ServerTemplate[] = [ + { + name :'index', + path : 'server.html' + }, + { + name : 'script', + path : 'lib/client/index.js' + } + ] + private http : Application + private httpd : Server + private wss : WebSocketServer + private port : number = 9900 + private wsPort : number = 9901 + private proxy : ServerProxyList = {} + private queue : ServerQueue = {} + private interval : ReturnType + private intervalPeriod : number = 10000 //10 sec + + constructor () { + this.init() + } + + async init () { + this.log = await Log({ label : this.id }); + await this.load() + await this.start() + } + + async load () { + this.http = express() + for (let tmpl of this.templates) { + tmpl.data = await readFile(tmpl.path, 'utf8') + } + + this.http.get('/', this.index.bind(this)) + this.http.get('/image/:key', this.image.bind(this)) + + this.log.info("Server assets loaded") + } + + template (name: string, data : ServerData) { + let html : string = this.templates.find(el => el.name === name).data + for (let key of Object.keys(data)) { + html = html.replace(`{{${key}}}`, data[key]) + } + return html + } + + async startWss () { + try { + this.wss = new WebSocketServer({ port: this.wsPort }) + } catch (err) { + this.log.error(err) + return + } + this.wss.on('connection', async function (ws : WebSocket) { + ws.on("message", function (data : string ) { + let obj : any = JSON.parse(data) + //this.log.info(data) + if (obj.id && this.queue[obj.id]) { + this.queue[obj.id](obj) + delete this.queue[obj.id] + if (obj.action !== 'ping') { + this.log.info(`${obj.action} ACK`) + } + } + }.bind(this)) + + ws.on('close', function () { + this.log.info('Client disconnected') + }.bind(this)) + + await this.cmd(ws, 'mcopy') + this.log.info('Client connected') + + }.bind(this)) + this.log.info(`Websocket server started!`) + this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`) + } + + async startHttp () { + return new Promise(function (resolve : Function, reject : Function) { + this.httpd = this.http.listen(this.port, function () { + this.log.info(`HTTP server started!`) + this.log.info(`URL [ http://localhost:${this.port} ]`) + return resolve(true) + }.bind(this)) + }.bind(this)) + } + + async start () { + await this.startHttp() + await this.startWss() + this.interval = setInterval(async function () { + await this.cmdAll('ping'); + }.bind(this), this.intervalPeriod) + this.isActive = true + } + + async stopHttp() { + return new Promise(function (resolve : Function, reject : Function) { + return this.httpd.close(function () { + this.log.info(`HTTP server stopped :(`) + return resolve(false) + }.bind(this)) + }.bind(this)) + } + + async stop () { + await this.stopHttp() + clearInterval(this.interval) + this.isActive = false + } + + index (req : Request, res : Response, next : Function) { + const SCRIPT = this.template('script', { PORT : `${this.wsPort}` }) + const html : string = this.template('index', { SCRIPT }) + this.log.info('GET /') + return res.send(html) + } + + async image (req : Request, res : Response, next : Function) { + let filePath : string + if (req.params && req.params.key) { + if (this.proxy[req.params.key]) { + filePath = this.proxy[req.params.key].path + } else { + return false + } + } else { + return false + } + return new Promise(function (resolve : Function, reject: Function) { + return res.sendFile(filePath, function (err : any) { + if (err) { + res.status(err.status).end() + return reject(err) + } + return resolve(true) + }) + }.bind(this)) + } + + public addProxy (key : string, filePath : string) { + //wipe every time + this.proxy = {} + this.proxy[key] = { + path : filePath + } + this.log.info(`Added proxy image [${key}]`) + } + + public async cmdAll (action : string, options : any = {}) { + const cmds : any[] = [] + if (this.useServer()) { + this.wss.clients.forEach(function (ws : WebSocket) { + cmds.push(this.cmd(ws, action, options)) + }.bind(this)) + await Promise.all(cmds) + return true + } + return false + } + + public async displayImage (src : string) { + let key + if (this.useServer()) { + key = basename(src) + this.addProxy(key, src) + await this.cmdAll('image', { image : `/image/${key}` }) + return true + } + return false + } + + public useServer () : boolean { + return this.isActive && this.wss.clients.size > 0 + } + + /** + * WSS + **/ + + public async cmd (ws : WebSocket, action : string, options : any = {}) { + const id : string = uuidv4() + let obj = { + id, action + } + let str : string + + obj = Object.assign(obj, options) + str = JSON.stringify(obj) + ws.send(str) + + return new Promise(function (resolve : Function, reject: Function) { + this.queue[id] = function (obj : any) { + return resolve(obj) + } + //setTimeout() ? + }.bind(this)) + } +} + +module.exports = function () { + return new Server() +} \ No newline at end of file