From c535a200e112b3bac5f504fdc94f4ffce610e893 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 27 Jul 2022 12:22:51 +0300 Subject: [PATCH] Move the business logic to ShellTransport --- src/services/Shell/Shell.js | 111 +++++++------------------ src/services/Shell/ShellTransport.js | 120 +++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 83 deletions(-) create mode 100644 src/services/Shell/ShellTransport.js diff --git a/src/services/Shell/Shell.js b/src/services/Shell/Shell.js index 8dc97d24e..99fc44721 100644 --- a/src/services/Shell/Shell.js +++ b/src/services/Shell/Shell.js @@ -1,14 +1,31 @@ // Copyright (C) 2017-2022 Smart code 203358507 const EventEmitter = require('eventemitter3'); +const ShellTransport = require('./ShellTransport'); function Shell() { let active = false; let error = null; let starting = false; + let transport = null; const events = new EventEmitter(); + function onTransportInit() { + active = true; + error = null; + starting = false; + onStateChanged(); + } + function onTransportInitError(err) { + console.error(err); + active = false; + error = err; + starting = false; + onStateChanged(); + transport = null; + } + function onStateChanged() { events.emit('stateChanged'); } @@ -34,6 +51,13 @@ function Shell() { get: function() { return starting; } + }, + transport: { + configurable: false, + enumerable: true, + get: function() { + return transport; + } } }); @@ -43,8 +67,10 @@ function Shell() { } active = false; - error = new Error('Stremio Shell API not available'); - starting = false; + starting = true; + transport = new ShellTransport(); + transport.on('init', onTransportInit); + transport.on('init-error', onTransportInitError); onStateChanged(); }; this.stop = function() { @@ -59,87 +85,6 @@ function Shell() { this.off = function(name, listener) { events.off(name, listener); }; - - this.props = {}; - const shell = this; - window.initShellComm = function () { - var transport = window.qt && window.qt.webChannelTransport; - if (!transport) throw 'no viable transport found (qt.webChannelTransport)'; - - active = false; - starting = true; - error = null; - - var QtMsgTypes = { - signal: 1, - propertyUpdate: 2, - init: 3, - idle: 4, - debug: 5, - invokeMethod: 6, - connectToSignal: 7, - disconnectFromSignal: 8, - setProperty: 9, - response: 10, - }; - var QtObjId = 'transport'; // the ID of our transport object - - var id = 0; - function send(msg) { - msg.id = id++; - transport.send(JSON.stringify(msg)); - } - - transport.onmessage = function (message) { - var msg = JSON.parse(message.data); - if (msg.id === 0) { - var obj = msg.data[QtObjId]; - - obj.properties.slice(1).forEach(function (prop) { - shell.props[prop[1]] = prop[3]; - }); - if (typeof shell.props.shellVersion === 'string') { - shell.shellVersionArr = ( - shell.props.shellVersion.match(/(\d+)\.(\d+)\.(\d+)/) || [] - ) - .slice(1, 4) - .map(Number); - } - events.emit('received-props', shell.props); - - obj.signals.forEach(function (sig) { - send({ - type: QtMsgTypes.connectToSignal, - object: QtObjId, - signal: sig[1], - }); - }); - - var onEvent = obj.methods.filter(function (x) { - return x[0] === 'onEvent'; - })[0]; - - shell.send = function (ev, args) { - send({ - type: QtMsgTypes.invokeMethod, - object: QtObjId, - method: onEvent[1], - args: [ev, args || {}], - }); - }; - starting = false; - active = true; - error = null; - - shell.send('app-ready', {}); // signal that we're ready to take events - } - - if (msg.object === QtObjId && msg.type === QtMsgTypes.signal) - events.emit(msg.args[0], msg.args[1]); - onStateChanged(); - }; - send({ type: QtMsgTypes.init }); - }; } module.exports = Shell; diff --git a/src/services/Shell/ShellTransport.js b/src/services/Shell/ShellTransport.js new file mode 100644 index 000000000..6014ef9d4 --- /dev/null +++ b/src/services/Shell/ShellTransport.js @@ -0,0 +1,120 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const EventEmitter = require('eventemitter3'); + +let shellAvailable = false; +const shellEvents = new EventEmitter(); + +const QtMsgTypes = { + signal: 1, + propertyUpdate: 2, + init: 3, + idle: 4, + debug: 5, + invokeMethod: 6, + connectToSignal: 7, + disconnectFromSignal: 8, + setProperty: 9, + response: 10, +}; +const QtObjId = 'transport'; // the ID of our transport object + +window.initShellComm = function () { + delete window.initShellComm; + shellEvents.emit('availabilityChanged'); +}; + +const initialize = () => { + return new Promise((resolve) => { + function onShellAvailabilityChanged() { + shellEvents.off('availabilityChanged', onShellAvailabilityChanged); + shellAvailable = true; + resolve(); + } + if (shellAvailable) { + onShellAvailabilityChanged(); + } else { + shellEvents.on('availabilityChanged', onShellAvailabilityChanged); + } + }); +}; + +function ShellTransport() { + const events = new EventEmitter(); + + this.props = {}; + + const shell = this; + initialize() + .then(() => { + const transport = window.qt && window.qt.webChannelTransport; + if (!transport) throw 'no viable transport found (qt.webChannelTransport)'; + + let id = 0; + function send(msg) { + msg.id = id++; + transport.send(JSON.stringify(msg)); + } + + transport.onmessage = function (message) { + const msg = JSON.parse(message.data); + if (msg.id === 0) { + const obj = msg.data[QtObjId]; + + obj.properties.slice(1).forEach(function (prop) { + shell.props[prop[1]] = prop[3]; + }); + if (typeof shell.props.shellVersion === 'string') { + shell.shellVersionArr = ( + shell.props.shellVersion.match(/(\d+)\.(\d+)\.(\d+)/) || [] + ) + .slice(1, 4) + .map(Number); + } + events.emit('received-props', shell.props); + + obj.signals.forEach(function (sig) { + send({ + type: QtMsgTypes.connectToSignal, + object: QtObjId, + signal: sig[1], + }); + }); + + const onEvent = obj.methods.filter(function (x) { + return x[0] === 'onEvent'; + })[0]; + + shell.send = function (ev, args) { + send({ + type: QtMsgTypes.invokeMethod, + object: QtObjId, + method: onEvent[1], + args: [ev, args || {}], + }); + }; + + shell.send('app-ready', {}); // signal that we're ready to take events + } + + if (msg.object === QtObjId && msg.type === QtMsgTypes.signal) + events.emit(msg.args[0], msg.args[1]); + events.emit('init'); + }; + send({ type: QtMsgTypes.init }); + }) .catch((error) => { + events.emit('init-error', error); + }); + + this.on = function(name, listener) { + events.on(name, listener); + }; + this.off = function(name, listener) { + events.off(name, listener); + }; + this.removeAllListeners = function() { + events.removeAllListeners(); + }; +} + +module.exports = ShellTransport;