From 5e37b8b3b97a3e25a164f5bc7fc17804404f0527 Mon Sep 17 00:00:00 2001 From: Moe Date: Thu, 13 Jun 2024 21:48:37 -0700 Subject: [PATCH] Add Wall View API Endpoint --- libs/webServer.js | 2 + libs/webServerStreamPaths.js | 30 ++++++++ web/assets/css/bs5.wallview.css | 70 +++++++++++++++++++ web/assets/js/bs5.wallview.js | 119 ++++++++++++++++++++++++++++++++ web/pages/wallview.ejs | 65 +++++++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 web/assets/css/bs5.wallview.css create mode 100644 web/assets/js/bs5.wallview.js create mode 100644 web/pages/wallview.ejs diff --git a/libs/webServer.js b/libs/webServer.js index 32dd7fe3..8761126c 100644 --- a/libs/webServer.js +++ b/libs/webServer.js @@ -54,6 +54,8 @@ module.exports = function(s,config,lang,io){ if(config.renderPaths.grid === undefined){config.renderPaths.grid='pages/grid'} //slick.js (cycle) page if(config.renderPaths.cycle === undefined){config.renderPaths.cycle='pages/cycle'} + //WallView page + if(config.renderPaths.wallview === undefined){config.renderPaths.wallview='pages/wallview'} // Use uws/cws if(config.useUWebsocketJs === undefined){config.useUWebsocketJs=true} if(config.webBlocksPreloaded === undefined){ diff --git a/libs/webServerStreamPaths.js b/libs/webServerStreamPaths.js index 1d7b8478..06e7d706 100644 --- a/libs/webServerStreamPaths.js +++ b/libs/webServerStreamPaths.js @@ -415,4 +415,34 @@ module.exports = function(s,config,lang,app){ },res,req); },res,req); }); + /** + * Page : Get WallView + */ + app.get(config.webPaths.apiPrefix+':auth/wallview/:ke', function (req,res){ + s.auth(req.params,function(user){ + const authKey = req.params.auth + const groupKey = req.params.ke + if( + user.permissions.watch_stream === "0" + || user.details.sub + && user.details.allmonitors !== '1' + ){ + res.end(user.lang['Not Permitted']) + return + } + s.renderPage(req,res,config.renderPaths.wallview,{ + forceUrlPrefix: req.query.host || '', + data: req.params, + protocol: req.protocol, + baseUrl: req.protocol + '://' + req.hostname, + config: s.getConfigWithBranding(req.hostname), + define: s.getDefinitonFile(user.details ? user.details.lang : config.lang), + lang: lang, + $user: user, + authKey: authKey, + groupKey: groupKey, + originalURL: s.getOriginalUrl(req) + }); + },res,req); + }); } diff --git a/web/assets/css/bs5.wallview.css b/web/assets/css/bs5.wallview.css new file mode 100644 index 00000000..01034468 --- /dev/null +++ b/web/assets/css/bs5.wallview.css @@ -0,0 +1,70 @@ +#wallview-container { + position: fixed; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.9); +} + +#wallview-canvas { + position: relative; + width: 100vw; + height: 100vh; + flex-grow: 1; + overflow: hidden; + flex: 1; +} + +#wallview-controls{ + position: absolute; + top: 20px; + right: 20px; + opacity: 0; + transition: 0.2s; +} + +#wallview-controls:hover{ + opacity: 100%; +} + +#wallview-monitorList i{ + display: none; +} + +#wallview-monitorList .active i{ + display: inlin-block; +} + +#wallview-canvas .wallview-video { + position: absolute; +} +#wallview-canvas .wallview-video .overlay { + position: absolute; + height: 100%; + width: 100%; + border: 0; +} +#wallview-canvas .wallview-video iframe { + height: 100%; + width: 100%; + border: 0; +} + +#wallview-canvas .wallview-video.col-md-4 { + height:30vh; +} +#wallview-canvas .wallview-video.col-md-6 { + height: 40vh; +} +#wallview-canvas .wallview-video.col-md-12 { + height: 80vh; + margin-bottom: 0.5rem !important; +} +#wallview-canvas .wallview-video:not(.no-video){ + background-color: #000!important; +} +#wallview-canvas .wallview-video.no-video{ + display: none; +} +#wallview-canvas.show-non-playing .wallview-video.no-video{ + display: flex; +} diff --git a/web/assets/js/bs5.wallview.js b/web/assets/js/bs5.wallview.js new file mode 100644 index 00000000..788b9296 --- /dev/null +++ b/web/assets/js/bs5.wallview.js @@ -0,0 +1,119 @@ +var loadedMonitors = {} +var selectedMonitors = {} +$(document).ready(function(){ + var wallViewMonitorList = $('#wallview-monitorList') + var wallViewControls = $('#wallview-controls') + var wallViewCanvas = $('#wallview-canvas') + function getApiPrefix(innerPart){ + return `${urlPrefix}${authKey}${innerPart ? `/${innerPart}/${groupKey}` : ''}` + } + function getWindowName(){ + const urlParams = new URLSearchParams(window.location.search); + const theWindow = urlParams.get('window'); + return theWindow || '1' + } + function drawMonitorListItem(monitor){ + wallViewMonitorList.append(`
  • ${monitor.name}
  • `) + } + function drawMonitorList(){ + return new Promise((resolve) => { + $.get(getApiPrefix('monitor'),function(monitors){ + $.each(monitors, function(n,monitor){ + if(monitor.mode !== 'stop' && monitor.mode !== 'idle'){ + loadedMonitors[monitor.mid] = monitor; + drawMonitorListItem(monitor) + } + }) + resolve(monitors) + }) + }) + } + + function getMonitorListItem(monitorId){ + return wallViewMonitorList.find(`[select-monitor="${monitorId}"]`) + } + + function selectMonitor(monitorId, css){ + css = css || {}; + selectedMonitors[monitorId] = Object.assign({}, loadedMonitors[monitorId]); + wallViewCanvas.append(`
    `) + wallViewCanvas.find(`[live-stream="${monitorId}"]`) + .draggable({ + grid: [10, 10], + snap: '#wallview-canvas', + containment: "window", + stop: function(){ + saveLayout() + } + }) + .resizable({ + grid: [10, 10], + snap: '#wallview-canvas', + containment: "window", + stop: function(){ + saveLayout() + } + }) + getMonitorListItem(monitorId).addClass('active') + } + function deselectMonitor(monitorId){ + delete(selectedMonitors[monitorId]) + var monitorItem = wallViewCanvas.find(`[live-stream="${monitorId}"]`); + monitorItem.find('iframe').attr('src','about:blank') + monitorItem.remove() + getMonitorListItem(monitorId).removeClass('active') + } + + function getCurrentLayout(){ + var layout = [] + wallViewCanvas.find('.wallview-video').each(function(n,v){ + var el = $(v) + var monitorId = el.attr('live-stream') + var position = el.position() + console.log(monitorId,position) + layout.push({ + monitorId, + css: { + left: position.left, + top: position.top, + width: el.width(), + height: el.height(), + } + }) + }) + return layout + } + + function saveLayout(){ + var windowName = getWindowName(); + var layout = getCurrentLayout(); + localStorage.setItem(`windowLayout_${windowName}`, JSON.stringify(layout)) + } + + function getLayout(){ + var windowName = getWindowName(); + var layout = JSON.parse(localStorage.getItem(`windowLayout_${windowName}`) || '[]') + return layout; + } + + function loadSavedLayout(){ + var layout = getLayout() + layout.forEach(function({ monitorId, css }, n){ + selectMonitor(monitorId, css) + }) + } + + drawMonitorList().then(loadSavedLayout) + $('body').on('click', '[select-monitor]', function(e){ + e.preventDefault() + var el = $(this); + var monitorId = el.attr('select-monitor') + var isSelected = selectedMonitors[monitorId] + if(isSelected){ + deselectMonitor(monitorId) + }else{ + selectMonitor(monitorId) + } + saveLayout() + }) +}) diff --git a/web/pages/wallview.ejs b/web/pages/wallview.ejs new file mode 100644 index 00000000..5161f3ce --- /dev/null +++ b/web/pages/wallview.ejs @@ -0,0 +1,65 @@ +<%- lang.Shinobi %> +<% + var forceUrlPrefix + var urlPrefix = `` + var targetPort = config.ssl && config.ssl.port && protocol === 'https' ? config.ssl.port : config.port + var addonsEnabled = {} + var rawAddonList = decodeURI(data.addon || '').split('|'); + rawAddonList.forEach(function(piece){ + var pieceParts = piece.split('='); + var key = pieceParts[0]; + var value = pieceParts[1] || true; + addonsEnabled[key] = value; + }); + function getAddon(addonTag){ + return addonsEnabled[addonTag]; + } + var streamWidth = parseInt(getAddon('width')) || 640 + var streamHeight = parseInt(getAddon('height')) || 480 + var hasGUI = getAddon('gui') + var isFullscreen = getAddon('fullscreen') + var isRelativeUrl = getAddon('relative') + if(forceUrlPrefix){ + urlPrefix = forceUrlPrefix + }else if(isRelativeUrl){ + urlPrefix = '' + }else if(config.baseURL){ + urlPrefix = config.baseURL + }else if(!targetPort || targetPort === '' || targetPort == 80 || targetPort == 443){ + urlPrefix = baseUrl + }else{ + urlPrefix = `${baseUrl}:${targetPort}` + } + if(urlPrefix.endsWith('/') === false){ + urlPrefix += '/' + } + var originalURL = `${urlPrefix}` +%> +<%- include('blocks/header-favicon') %> + + + + + + + + + + +
    +
    + +
    + +
    + + + +