diff --git a/lib/saito/core/server.ts b/lib/saito/core/server.ts index 77231ca334..758dfac945 100644 --- a/lib/saito/core/server.ts +++ b/lib/saito/core/server.ts @@ -325,19 +325,17 @@ class Server { // eslint-disable-next-line @typescript-eslint/no-var-requires const ws = require('ws'); - const wss = new ws.Server({ + const wss = new ws.WebSocketServer({ noServer: true, path: '/wsopen' }); webserver.on('upgrade', (request: any, socket: any, head: any) => { - // console.debug("connection upgrade ----> " + request.url); + console.debug("connection upgrade ----> " + request.url); const { pathname } = parse(request.url); if (pathname === '/wsopen') { wss.handleUpgrade(request, socket, head, (websocket: any) => { wss.emit('connection', websocket, request); }); - } else { - socket.destroy(); } }); webserver.on('error', (error) => { @@ -367,6 +365,7 @@ class Server { }); + this.app.modules.onWebSocketServer(webserver); } initialize() { diff --git a/lib/saito/modules.ts b/lib/saito/modules.ts index 6f3de9b77a..3e0ed73b23 100644 --- a/lib/saito/modules.ts +++ b/lib/saito/modules.ts @@ -3,6 +3,8 @@ import Peer from './peer'; import Transaction from './transaction'; import path from 'path'; import fs from 'fs'; +import ws from 'ws'; +import { parse } from 'url'; class Mods { @@ -126,8 +128,8 @@ class Mods { } } - } catch (err) { } - + } catch (err) { + } for (let iii = 0; iii < this.mods.length; iii++) { @@ -283,11 +285,11 @@ class Mods { // // ... setup moderation / filter functions // - for (let xmod of this.app.modules.respondTo('saito-moderation-app')) { - this.app_filter_func.push(xmod.respondTo('saito-moderation-app').filter_func); + for (let xmod of this.app.modules.respondTo('saito-moderation-app')) { + this.app_filter_func.push(xmod.respondTo('saito-moderation-app').filter_func); } - for (let xmod of this.app.modules.respondTo('saito-moderation-core')) { - this.core_filter_func.push(xmod.respondTo('saito-moderation-core').filter_func); + for (let xmod of this.app.modules.respondTo('saito-moderation-core')) { + this.core_filter_func.push(xmod.respondTo('saito-moderation-core').filter_func); } // @@ -311,7 +313,7 @@ class Mods { 'handshake_complete', async (peerIndex: bigint) => { - if (this.app.BROWSER){ + if (this.app.BROWSER) { // broadcasts my keylist to other peers await this.app.wallet.setKeyList(this.app.keychain.returnWatchedPublicKeys()); } @@ -372,16 +374,18 @@ class Mods { // // 1 = permit, -1 = do not permit // - moderateModule(tx=null, mod=null) { + moderateModule(tx = null, mod = null) { - if (mod == null || tx == null) { return 0; } + if (mod == null || tx == null) { + return 0; + } for (let z = 0; z < this.app_filter_func.length; z++) { let permit_through = this.app_filter_func[z](mod, tx); - if (permit_through == 1) { + if (permit_through == 1) { return 1; } - if (permit_through == -1) { + if (permit_through == -1) { return -1; } } @@ -394,16 +398,18 @@ class Mods { // // 1 = permit, -1 = do not permit // - moderateCore(tx=null) { + moderateCore(tx = null) { - if (tx == null) { return 0; } + if (tx == null) { + return 0; + } for (let z = 0; z < this.core_filter_func.length; z++) { let permit_through = this.core_filter_func[z](tx); - if (permit_through == 1) { + if (permit_through == 1) { return 1; } - if (permit_through == -1) { + if (permit_through == -1) { return -1; } } @@ -412,14 +418,13 @@ class Mods { } - - moderateAddress(publickey="") { + moderateAddress(publickey = '') { let newtx = new Transaction(); newtx.addFrom(publickey); return this.moderate(newtx); } - moderate(tx=null, app="") { + moderate(tx = null, app = '') { let permit_through = 0; @@ -427,10 +432,14 @@ class Mods { // if there is a relevant app-filter-function, respect it // for (let i = 0; i < this.mods.length; i++) { - if (this.mods[i].name == app || app == "*") { + if (this.mods[i].name == app || app == '*') { permit_through = this.moderateModule(tx, this.mods[i]); - if (permit_through == -1) { return -1; } - if (permit_through == 1) { return 1; } + if (permit_through == -1) { + return -1; + } + if (permit_through == 1) { + return 1; + } } } @@ -439,9 +448,13 @@ class Mods { // permit_through = this.moderateCore(tx); - if (permit_through == -1) { return -1; } - if (permit_through == 1) { return 1; } - + if (permit_through == -1) { + return -1; + } + if (permit_through == 1) { + return 1; + } + // // seems OK if we made it this far // @@ -456,7 +469,7 @@ class Mods { await this.mods[icb].render(this.app, this.mods[icb]); } } - this.app.connection.emit("saito-render-complete"); + this.app.connection.emit('saito-render-complete'); return null; } @@ -697,6 +710,39 @@ class Mods { } } + async onWebSocketServer(webserver) { + for (let i = 0; i < this.mods.length; i++) { + let mod = this.mods[i]; + let path = mod.getWebsocketPath(); + if (!path) { + continue; + } + console.log('creating websocket server for module :' + mod.name + " on path : "+path); + let wss = new ws.WebSocketServer({ + noServer: true, + // todo : check if the path is already being used or reserved? + path: "/"+path + }); + webserver.on('upgrade', (request: any, socket: any, head: any) => { + console.debug("connection on module : "+mod.name+" upgrade ----> " + request.url); + const parsedUrl = parse(request.url); + const pathname = parsedUrl.pathname; + const pathParts = pathname.split('/').filter(Boolean); + const subdirectory = pathParts.length > 0 ? pathParts[0] : null; + console.log(subdirectory + " - " + path); + if (subdirectory === path) { + console.log('inside handleUpgrade'); + wss.handleUpgrade(request, socket, head, (websocket: any) => { + console.log("handling upgrade ///"); + wss.emit('connection', websocket, request); + }); + } + }); + + mod.onWebSocketServer(wss); + } + } + /* async getBuildNumber() { for (let i = 0; i < this.mods.length; i++) { diff --git a/lib/templates/modtemplate.js b/lib/templates/modtemplate.js index 45a55f3484..b8faf09d5f 100644 --- a/lib/templates/modtemplate.js +++ b/lib/templates/modtemplate.js @@ -37,7 +37,7 @@ class ModTemplate { midnight: 'fa-solid fa-moon', milquetoast: 'fa-solid fa-cow', sangre3000: 'fa-solid fa-droplet-slash' - + }; this.processedTxs = {}; @@ -245,8 +245,8 @@ class ModTemplate { return 'Unknown Module'; } - returnTitle(){ - if (this.title){ + returnTitle() { + if (this.title) { return this.title; } return this.returnName(); @@ -264,12 +264,14 @@ class ModTemplate { return false; } - loadSettings() {} + loadSettings() { + } // // INITIALIZE HTML (deprecated by render(app)) // - async initializeHTML(app) {} + async initializeHTML(app) { + } // // ATTACH EVENTS (deprecated by render(app)) @@ -278,7 +280,8 @@ class ModTemplate { // DOM, allowing us to incorporate the web applications to our own // internal functions and send and receive transactions natively. // - attachEvents(app) {} + attachEvents(app) { + } // // LOAD FROM ARCHIVES @@ -314,7 +317,8 @@ class ModTemplate { // in those transcations can also subscribe to those confirmations by // by using shouldAffixCallbackToModule. // - async onConfirmation(blk, tx, confnum) {} + async onConfirmation(blk, tx, confnum) { + } // // some UI elements may provide special display options for modules which @@ -336,7 +340,8 @@ class ModTemplate { // this is where the most important code in your module should go, // listening to requests that come in over the blockchain and replying. // - onNewBlock(blk, lc) {} + onNewBlock(blk, lc) { + } // // @@ -349,7 +354,8 @@ class ModTemplate { // and then it is run a second time setting the LC to 1 for all of the // blocks that are moved (back?) into the longest_chain // - onChainReorganization(block_id, block_hash, lc, pos) {} + onChainReorganization(block_id, block_hash, lc, pos) { + } // // @@ -386,7 +392,8 @@ class ModTemplate { // fetch service-level data like DNS information instead of having to blindly // guess or manually examine their peers. // - async onPeerServiceUp(app, peer, service) {} + async onPeerServiceUp(app, peer, service) { + } // // @@ -394,21 +401,24 @@ class ModTemplate { // ON ARCHIVE HANDSHAKE COMPLETE // // this function runs when a node completes its handshake with a peer that offers archiving services - onArchiveHandshakeComplete(app, peer) {} + onArchiveHandshakeComplete(app, peer) { + } // // // ON CONNECTION STABLE // // this function runs "connect" event - onConnectionStable(app, peer) {} + onConnectionStable(app, peer) { + } // // // ON CONNECTION UNSTABLE // // this function runs "disconnect" event - onConnectionUnstable(app, peer) {} + onConnectionUnstable(app, peer) { + } // // SHOULD AFFIX CALLBACK TO MODULE @@ -474,7 +484,8 @@ class ModTemplate { // blockchain syncing. It will be triggered on startup and with // every additional block added. // - updateBlockchainSync(app, current, target) {} + updateBlockchainSync(app, current, target) { + } ///////////////////////// // MODULE INTERACTIONS // @@ -576,7 +587,8 @@ class ModTemplate { // data out into this function, which can be overridden as needed in // order to // - receiveEvent(eventname, data) {} + receiveEvent(eventname, data) { + } // // DEPRECATED -- port to app.connection.emit('event', {}); @@ -741,7 +753,7 @@ class ModTemplate { return this.app.network.sendRequestAsTransaction( message.request, message.data, - function (res) { + function(res) { return mycallback(res); } ); @@ -749,7 +761,7 @@ class ModTemplate { return this.app.network.sendRequestAsTransaction( message.request, message.data, - function (res) { + function(res) { return mycallback(res); }, peer.peerIndex @@ -796,7 +808,8 @@ class ModTemplate { async sendPeerRequestWithServiceFilter( servicename, msg, - success_callback = (res) => {} + success_callback = (res) => { + } ) { this.sendPeerRequestWithFilter( () => { @@ -844,7 +857,7 @@ class ModTemplate { this.app.network.sendRequestAsTransaction( message.request, message.data, - function (res) { + function(res) { if (success_callback != null) { success_callback(res); } @@ -866,14 +879,14 @@ class ModTemplate { return this.app.network.sendRequestAsTransaction( message.request, message.data, - function (res) { + function(res) { //JSON.stringify("callback data1: " + JSON.stringify(res)); return mycallback(res); } ); } - isSlug(slug){ + isSlug(slug) { return (slug == this.returnSlug()); } @@ -900,17 +913,20 @@ class ModTemplate { } } - handleUrlParams(urlParams) {} + handleUrlParams(urlParams) { + } showAlert() { this.alerts++; try { let qs = '#' + this.returnSlug() + ' > .redicon'; document.querySelector(qs).style.display = 'block'; - } catch (err) {} + } catch (err) { + } } - attachMeta() {} + attachMeta() { + } attachStyleSheets() { if (this.stylesheetAdded === true) return; @@ -924,7 +940,8 @@ class ModTemplate { ) { should_attach_sheet = false; } - } catch (err) {} + } catch (err) { + } }); if (should_attach_sheet) { @@ -953,7 +970,8 @@ class ModTemplate { if (el.attributes.src.nodeValue === script) { script_attached = true; } - } catch (err) {} + } catch (err) { + } }); scriptCount++; if (!script_attached) { @@ -991,7 +1009,8 @@ class ModTemplate { if (el.attributes.src.nodeValue === script) { script_attached = true; } - } catch (err) {} + } catch (err) { + } }); if (!script_attached) { const s = document.createElement('script'); @@ -1012,7 +1031,8 @@ class ModTemplate { this.scriptsAdded = false; } - attachMeta(app) {} + attachMeta(app) { + } removeStyleSheets(app) { this.stylesheets.forEach((stylesheet) => { @@ -1023,7 +1043,8 @@ class ModTemplate { this.stylesheetAdded = false; } - removeMeta() {} + removeMeta() { + } removeEvents() { this.eventListeners.forEach((eventListener) => { @@ -1059,15 +1080,15 @@ class ModTemplate {
${warningText}
+ warningText.length == 0 + ? 'style=\'flex:1;\'' + : 'style=\'flex:2;\'' + }>${warningText} `; let overlay_self = this.overlay; @@ -1083,7 +1104,8 @@ class ModTemplate { try { document.getElementById('clock_number').innerHTML = Math.ceil(time / 1000); - } catch (err) {} + } catch (err) { + } }, 250); } @@ -1102,15 +1124,28 @@ class ModTemplate { } hasSeenTransaction(tx) { - let hashed_data = this.name + tx.signature; + let hashed_data = this.name + tx.signature; - if (this.processedTxs[hashed_data]) { - return true; + if (this.processedTxs[hashed_data]) { + return true; + } + this.processedTxs[hashed_data] = true; + + return false; } - this.processedTxs[hashed_data] = true; - return false; -} + getWebsocketPath() { + return ''; + } + + onWebSocketServer(wss) { + // wss.on('connection', (socket, request) => { + // socket.on('message', (msg) => { + // }); + // socket.on('close', () => {}); + // socket.on('error', (err) => {}); + // }); + } } diff --git a/mods/limbo/lib/lite-dream-controls.js b/mods/limbo/lib/lite-dream-controls.js index 03652bf3e5..6cbae85f2a 100644 --- a/mods/limbo/lib/lite-dream-controls.js +++ b/mods/limbo/lib/lite-dream-controls.js @@ -138,6 +138,10 @@ class DreamControls{ //Tell PeerManager to pause streams for green room this.app.connection.emit('limbo-toggle-audio'); this.app.connection.emit('limbo-toggle-video'); + + if (!document.querySelector('.dream-controls-menu-item')) { + this.app.connection.emit('saito-limbo-add-yt-icon'); + } } @@ -270,10 +274,6 @@ class DreamControls{ //Only necessary for first click but doesn't hurt to have this.startTimer(); // Start timer e.currentTarget.classList.remove("click-me"); - - if (!document.querySelector('.dream-controls-menu-item')) { - this.app.connection.emit('saito-limbo-add-yt-icon'); - } } } diff --git a/mods/limbo/web/css/limbo-base.css b/mods/limbo/web/css/limbo-base.css index 8cb31cbc1d..7cd4c1edb7 100644 --- a/mods/limbo/web/css/limbo-base.css +++ b/mods/limbo/web/css/limbo-base.css @@ -538,4 +538,10 @@ video#local.noflip{ .yt-active > i { animation: pulsate 2s ease-in-out infinite !important; +} + +.yt-stream-type { + display: flex; + gap: 1.5rem; + align-items: center; } \ No newline at end of file diff --git a/mods/spam/spam.js b/mods/spam/spam.js index 29959297f3..7d93364d7d 100644 --- a/mods/spam/spam.js +++ b/mods/spam/spam.js @@ -30,9 +30,9 @@ class Spam extends ModTemplate { this.styles = ['/spam/style.css', '/saito/saito.css']; } if (this.app.BROWSER == 0) { - setInterval(() => { - this.nodeSpamLoop(app, this); - }, 13000); + // setInterval(() => { + // this.nodeSpamLoop(app, this); + // }, 13000); } } diff --git a/mods/youtube-client/lib/yt-client-init-stream.js b/mods/youtube-client/lib/yt-client-init-stream.js index 2983aab34d..ab84594549 100644 --- a/mods/youtube-client/lib/yt-client-init-stream.js +++ b/mods/youtube-client/lib/yt-client-init-stream.js @@ -18,10 +18,25 @@ class YoutubeInitStream{ if (document.getElementById("yt-stream-btn")){ document.getElementById("yt-stream-btn").onclick = (e) => { let stream_key = document.getElementById("yt-stream-identifier")?.value; - + let stream_type = document.querySelector('input[name=stream_type]:checked').value;; + if (stream_key != "") { - this_self.app.connection.emit("saito-yt-start-stream", {stream_key: stream_key}); + + console.log({ + stream_key: stream_key, + stream_type: stream_type + }); + + this_self.app.connection.emit("saito-yt-start-stream", { + stream_key: stream_key, + stream_type: stream_type + }); this_self.overlay.close(); + + this_self.app.options.youtube = {}; + this_self.app.options.youtube.stream_key = stream_key; + + this_self.app.storage.saveOptions(); } else { salert("Please provide a valid stream key"); } diff --git a/mods/youtube-client/lib/yt-client-init-stream.template.js b/mods/youtube-client/lib/yt-client-init-stream.template.js index f9604e220e..25bc0090ff 100644 --- a/mods/youtube-client/lib/yt-client-init-stream.template.js +++ b/mods/youtube-client/lib/yt-client-init-stream.template.js @@ -1,9 +1,23 @@ module.exports = YoutubeInitStreamTemplate = (app,mod) => { + if (app.options?.youtube?.stream_key != null) { + console.log("previous stream key:", app.options.youtube.stream_key); + } + let html = `