From de9577efe5c34ddc9d9e49d0699bca3ccddca232 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Sat, 9 Jul 2022 23:16:43 +0300 Subject: [PATCH] core ran in a worker thread --- src/App/App.js | 14 ++++----- src/services/Core/Core.js | 4 +-- src/services/Core/CoreTransport.js | 49 ++++++++++++++---------------- src/services/Core/bridge.js | 45 +++++++++++++++++++++++++++ src/services/Core/worker.js | 23 ++++++++++++++ webpack.config.js | 5 ++- 6 files changed, 102 insertions(+), 38 deletions(-) create mode 100644 src/services/Core/bridge.js create mode 100644 src/services/Core/worker.js diff --git a/src/App/App.js b/src/App/App.js index a07435d2b..570c655f0 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -5,24 +5,22 @@ const React = require('react'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); -const { ToastProvider, sanitizeLocationPath, CONSTANTS } = require('stremio/common'); +const { ToastProvider, CONSTANTS } = require('stremio/common'); const CoreEventsToaster = require('./CoreEventsToaster'); const ErrorDialog = require('./ErrorDialog'); const routerViewsConfig = require('./routerViewsConfig'); const styles = require('./styles'); -window.core_imports = { - app_version: process.env.VERSION, - shell_version: null, - sanitize_location_path: sanitizeLocationPath -}; - const App = () => { const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); const services = React.useMemo(() => ({ - core: new Core(), + core: new Core({ + baseURI: document.baseURI, + appVersion: process.env.VERSION, + shellVersion: null + }), shell: new Shell(), chromecast: new Chromecast(), keyboardShortcuts: new KeyboardShortcuts() diff --git a/src/services/Core/Core.js b/src/services/Core/Core.js index 9716cf2b3..e033396d5 100644 --- a/src/services/Core/Core.js +++ b/src/services/Core/Core.js @@ -3,7 +3,7 @@ const EventEmitter = require('eventemitter3'); const CoreTransport = require('./CoreTransport'); -function Core() { +function Core(args) { let active = false; let error = null; let starting = false; @@ -66,7 +66,7 @@ function Core() { } starting = true; - transport = new CoreTransport(); + transport = new CoreTransport(args); transport.on('init', onTransportInit); transport.on('error', onTransportError); onStateChanged(); diff --git a/src/services/Core/CoreTransport.js b/src/services/Core/CoreTransport.js index 488b730f1..7a3a0c0ea 100644 --- a/src/services/Core/CoreTransport.js +++ b/src/services/Core/CoreTransport.js @@ -1,19 +1,22 @@ // Copyright (C) 2017-2022 Smart code 203358507 const EventEmitter = require('eventemitter3'); -const { default: initialize_api, initialize_runtime, get_state, get_debug_state, dispatch, analytics, decode_stream } = require('@stremio/stremio-core-web'); +const Bridge = require('./bridge'); -function CoreTransport() { +function CoreTransport(args) { const events = new EventEmitter(); + const worker = new Worker(`${process.env.COMMIT_HASH}/scripts/worker.js`); + const bridge = new Bridge(worker, window); - initialize_api(require('@stremio/stremio-core-web/stremio_core_web_bg.wasm')) - .then(() => initialize_runtime(({ name, args }) => { - try { - events.emit(name, args); - } catch (error) { - console.error('CoreTransport', error); - } - })) + window.onCoreEvent = ({ name, args }) => { + try { + events.emit(name, args); + } catch (error) { + console.error('CoreTransport', error); + } + }; + + bridge.call(['init'], [args]) .then(() => { try { events.emit('init'); @@ -35,27 +38,19 @@ function CoreTransport() { events.removeAllListeners(); }; this.getState = async function(field) { - return Promise.resolve(get_state(field)); + return bridge.call(['getState'], [field]); }; - this.getDebugState = function() { - return get_debug_state(); + this.getDebugState = async function() { + return bridge.call(['getDebugState'], []); }; - this.dispatch = function(action, field) { - try { - dispatch(action, field); - } catch (error) { - console.error('CoreTransport', error); - } + this.dispatch = async function(action, field) { + return bridge.call(['dispatch'], [action, field, location.hash]); }; - this.analytics = function(event) { - try { - analytics(event); - } catch (error) { - console.error('CoreTransport', error); - } + this.analytics = async function(event) { + return bridge.call(['analytics'], [event, location.hash]); }; - this.decodeStream = function(stream) { - return decode_stream(stream); + this.decodeStream = async function(stream) { + return bridge.call(['decodeStream'], [stream]); }; } diff --git a/src/services/Core/bridge.js b/src/services/Core/bridge.js new file mode 100644 index 000000000..e9fda2d63 --- /dev/null +++ b/src/services/Core/bridge.js @@ -0,0 +1,45 @@ +function getId() { + return Math.random().toString(32).slice(2); +} + +function Bridge(context, scope) { + context.addEventListener('message', async ({ data: { request } }) => { + if (!request) return; + + const { id, path, args } = request; + try { + const object = path.reduce((obj, prop) => obj[prop], scope); + let data; + if (typeof object === 'function') { + const thisArg = path.slice(0, path.length - 1).reduce((obj, prop) => obj[prop], scope); + data = await object.apply(thisArg, args); + } else { + data = await object; + } + + context.postMessage({ response: { id, result: { data } } }); + } catch (error) { + context.postMessage({ response: { id, result: { error } } }); + } + }); + + this.call = async (path, args) => { + const id = getId(); + return new Promise((resolve, reject) => { + const onMessage = ({ data: { response } }) => { + if (!response || response.id !== id) return; + + context.removeEventListener('message', onMessage); + if ('error' in response.result) { + reject(response.result.error); + } else { + resolve(response.result.data); + } + }; + context.addEventListener('message', onMessage); + context.postMessage({ request: { id, path, args } }); + }); + }; +} + +module.exports = Bridge; diff --git a/src/services/Core/worker.js b/src/services/Core/worker.js new file mode 100644 index 000000000..017a283c3 --- /dev/null +++ b/src/services/Core/worker.js @@ -0,0 +1,23 @@ +const sanitizeLocationPath = require('stremio/common/sanitizeLocationPath'); +const Bridge = require('./bridge'); + +const bridge = new Bridge(self, self); + +self.init = async ({ baseURI, appVersion, shellVersion }) => { + self.document = { baseURI }; + self.app_version = appVersion; + self.shell_version = shellVersion; + self.sanitize_location_path = sanitizeLocationPath; + self.get_location_hash = async () => bridge.call(['location', 'hash'], []); + self.local_storage_get_item = async (key) => bridge.call(['localStorage', 'getItem'], [key]); + self.local_storage_set_item = async (key, value) => bridge.call(['localStorage', 'setItem'], [key, value]); + self.local_storage_remove_item = async (key) => bridge.call(['localStorage', 'removeItem'], [key]); + const { default: initialize_api, initialize_runtime, get_state, get_debug_state, dispatch, analytics, decode_stream } = require('@stremio/stremio-core-web'); + self.getState = get_state; + self.getDebugState = get_debug_state; + self.dispatch = dispatch; + self.analytics = analytics; + self.decodeStream = decode_stream; + await initialize_api(require('@stremio/stremio-core-web/stremio_core_web_bg.wasm')); + await initialize_runtime((event) => bridge.call(['onCoreEvent'], [event])); +}; diff --git a/webpack.config.js b/webpack.config.js index f9e53bfb8..29fd80ca4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -15,7 +15,10 @@ const COMMIT_HASH = execSync('git rev-parse HEAD').toString().trim(); module.exports = (env, argv) => ({ mode: argv.mode, devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map', - entry: './src/index.js', + entry: { + main: './src/index.js', + worker: './src/services/Core/worker.js' + }, output: { path: path.join(__dirname, 'build'), filename: `${COMMIT_HASH}/scripts/[name].js`