mild refactor for embed live stream feature

fix-timezone-by-ui
Moe 2022-10-08 16:28:12 -07:00
parent 49b518d595
commit 3a7d30e362
10 changed files with 1068 additions and 423 deletions

View File

@ -39,6 +39,8 @@ module.exports = function(s,config,lang,app){
app.get([config.webPaths.apiPrefix+':auth/embed/:ke/:id',config.webPaths.apiPrefix+':auth/embed/:ke/:id/:addon'], function (req,res){
req.params.protocol=req.protocol;
s.auth(req.params,function(user){
const authKey = req.params.auth
const groupKey = req.params.ke
const monitorId = req.params.id
if(cantLiveStreamPermission(user,monitorId)){
s.closeJsonResponse(res,{ok: false, msg: lang['Not Authorized']});
@ -52,7 +54,21 @@ module.exports = function(s,config,lang,app){
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[req.params.id]){
if(s.group[req.params.ke].activeMonitors[req.params.id].isStarted === true){
req.params.uid=user.uid;
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config: s.getConfigWithBranding(req.hostname),lang:user.lang,mon:Object.assign(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{}),originalURL:s.getOriginalUrl(req)});
s.renderPage(req,res,config.renderPaths.embed,{
data: req.params,
baseUrl: req.protocol+'://'+req.hostname,
config: s.getConfigWithBranding(req.hostname),
define: s.getDefinitonFile(user.details ? user.details.lang : config.lang),
$user: {
auth_token: authKey,
ke: groupKey,
uid: user.uid,
mail: user.mail,
details: {},
},
mon: Object.assign(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{}),
originalURL: s.getOriginalUrl(req)
});
res.end()
}else{
res.end(user.lang['Cannot watch a monitor that isn\'t running.'])

View File

@ -0,0 +1,72 @@
.progress-bar {
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
color: #fff;
text-align: center;
white-space: nowrap;
background-color: #1f80f9;
transition: width 0.6s ease;
}
#video_preview .stream-objects {
right: 0;
margin: auto;
display: inline-block;
position: relative;
width: auto
}
.stream-block,
.stream-objects {
overflow: hidden !important
}
.stream-objects {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10
}
.stream-objects .tag {
position: absolute;
top: 5px;
left: 5px;
background: #d9534f;
color: #fff;
font-family: monospace;
font-size: 80%;
border-radius: 15px;
padding: 3px 5px;
line-height: 1
}
.stream-objects .stream-detected-object {
position: absolute;
top: 0;
left: 0;
border: 3px dotted red;
background: transparent;
border-radius: 5px
}
.stream-objects .stream-detected-point {
position: absolute;
top: 0;
left: 0;
border: 3px solid yellow;
background: transparent;
border-radius: 5px
}
.stream-objects .point {
position: absolute;
top: 0;
left: 0;
border: 3px solid red;
border-radius: 50%
}

View File

@ -0,0 +1,24 @@
body,
html {
overflow: hidden;
height: 100%;
}
* {
margin: 0;
padding: 0;
border: 0
}
.stream-element,
.shinobi_stream {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
.shinobi_stream video {
object-fit: fill
}

View File

@ -0,0 +1,102 @@
body {
position: relative;
}
.shinobi_stream {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.shinobi_hud {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
transition: 0.2s
}
.shinobi_hud:hover {
opacity: 1
}
.shinobi_hud .shinobi_viewers {
position: absolute;
top: 5px;
left: 5px;
}
.shinobi_hud .shinobi_viewers {
display: inline-block;
min-width: 10px;
padding: 3px 7px;
font-size: 12px;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: middle;
background-color: #777;
border-radius: 10px;
font-family: sans-serif;
}
iframe.stream-element {
border: 0;
}
/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */
.shinobi_ws_http_toggle {
width: 50px;
height: 20px;
position: absolute;
bottom: 10px;
left: 10px;
}
.shinobi_ws_http_toggle input[type=checkbox] {
height: 0;
width: 0;
visibility: hidden;
}
.shinobi_ws_http_toggle label {
cursor: pointer;
text-indent: -9999px;
width: 100px;
height: 20px;
background: grey;
display: block;
border-radius: 100px;
position: relative;
}
.shinobi_ws_http_toggle label:after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
background: #fff;
border-radius: 90px;
transition: 0.3s;
}
.shinobi_ws_http_toggle input:checked+label {
background: #00118c;
}
.shinobi_ws_http_toggle input:checked+label:after {
left: calc(100% - 5px);
transform: translateX(-100%);
}
.shinobi_ws_http_toggle label:active:after {
width: 10px;
}

View File

@ -0,0 +1,4 @@
$(window).resize(function(){
$('.stream-element').attr('width',$('body').width())
$('.stream-element').attr('height',$('body').height())
})

722
web/assets/js/bs5.embed.js Normal file
View File

@ -0,0 +1,722 @@
var loadedLiveGrids = {}
var monitorPops = {}
var liveGridElements = {}
var runningJpegStreams = {}
var liveGrid = $('#monitors_live .stream-element-container')
//
var onLiveStreamInitiateExtensions = []
function onLiveStreamInitiate(callback){
onLiveStreamInitiateExtensions.push(callback)
}
var onLiveStreamCloseExtensions = []
function onLiveStreamClose(callback){
onLiveStreamCloseExtensions.push(callback)
}
var onSignalCheckLiveStreamExtensions = []
function onSignalCheckLiveStream(callback){
onSignalCheckLiveStreamExtensions.push(callback)
}
var onBuildStreamElementExtensions = []
function onBuildStreamElement(callback){
onBuildStreamElementExtensions.push(callback)
}
//
function buildStreamElementHtml(streamType){
var html = ''
if(window.jpegModeOn === true){
html = '<img class="stream-element">';
}else{
switch(streamType){
case'hls':case'flv':case'mp4':
html = `<video class="stream-element" playsinline muted autoplay></video>`;
break;
case'mjpeg':
html = '<iframe class="stream-element"></iframe>';
break;
case'jpeg':
html = '<img class="stream-element">';
break;
default://base64//h265
html = '<canvas class="stream-element"></canvas>';
break;
}
$.each(onBuildStreamElementExtensions,function(n,extender){
var newHtml = extender(streamType)
html = newHtml ? newHtml : html
})
}
return html
}
function resetMonitorCanvas(monitorId,initiateAfter,subStreamChannel){
var monitor = loadedMonitors[monitorId]
var details = monitor.details
var streamType = subStreamChannel ? details.substream ? details.substream.output.stream_type : 'hls' : details.stream_type
if(!liveGridElements[monitorId])return;
var streamBlock = liveGridElements[monitorId].monitorItem.find('.stream-block')
closeLiveGridPlayer(monitorId,false)
streamBlock.find('.stream-element').remove()
streamBlock.append(buildStreamElementHtml(streamType))
if(initiateAfter)initiateLiveGridPlayer(monitor,subStreamChannel)
}
function buildLiveGridBlock(monitor){
if(monitor.mode === 'stop'){
new PNotify({
title: lang.sorryNo,
text: lang[`Cannot watch a monitor that isn't running.`],
type: 'danger'
})
return
}
var monitorId = monitor.mid
var monitorDetails = safeJsonParse(monitor.details)
var monitorLiveId = `monitor_live_${monitor.mid}`
var subStreamChannel = monitor.subStreamChannel
var streamType = subStreamChannel ? monitorDetails.substream ? monitorDetails.substream.output.stream_type : 'hls' : monitorDetails.stream_type
var streamElement = buildStreamElementHtml(streamType)
var streamBlockInfo = definitions['Monitor Stream Window']
if(!loadedLiveGrids[monitor.mid])loadedLiveGrids[monitor.mid] = {}
var quickLinkHtml = ''
var baseHtml = `<div
id="${monitorLiveId}"
data-ke="${monitor.ke}"
data-mid="${monitor.mid}"
data-mode="${monitor.mode}"
class="monitor_item glM${monitor.mid} ${streamBlockInfo.gridBlockClass || ''}"
>
<div class="stream-objects"></div>
<div class="stream-hud">
${streamBlockInfo.streamBlockHudHtml || ''}
</div>
${streamElement}
${(streamBlockInfo.gridBlockAfterContentHtml || '').replace(`$QUICKLINKS`,quickLinkHtml)}
</div>`
return baseHtml
}
function drawLiveGridBlock(monitorConfig,subStreamChannel){
var monitorId = monitorConfig.mid
if($('#monitor_live_' + monitorId).length === 0){
var html = buildLiveGridBlock(monitorConfig)
liveGrid.html(html);
var theBlock = $('#monitor_live_' + monitorId);
var streamElement = theBlock.find('.stream-element')
liveGridElements[monitorId] = {
monitorItem: theBlock,
streamElement: streamElement,
eventObjects: theBlock.find('.stream-objects'),
motionMeter: theBlock.find('.indifference .progress-bar'),
motionMeterText: theBlock.find('.indifference .progress-bar span'),
width: streamElement.width(),
height: streamElement.height(),
}
}
initiateLiveGridPlayer(loadedMonitors[monitorId],subStreamChannel)
}
function initiateLiveGridPlayer(monitor,subStreamChannel){
var livePlayerElement = loadedLiveGrids[monitor.mid]
var details = monitor.details
var groupKey = monitor.ke
var monitorId = monitor.mid
var loadedMonitor = loadedMonitors[monitorId]
var loadedPlayer = loadedLiveGrids[monitor.mid]
var websocketPath = checkCorrectPathEnding(location.pathname) + 'socket.io'
var containerElement = $(`#monitor_live_${monitor.mid}`)
var streamType = subStreamChannel ? details.substream ? details.substream.output.stream_type : 'hls' : details.stream_type
if(location.search === '?p2p=1'){
websocketPath = '/socket.io'
// websocketQuery.machineId = machineId
}
switch(streamType){
case'jpeg':
startJpegStream(monitorId)
break;
case'b64':
if(loadedPlayer.Base64 && loadedPlayer.Base64.connected){
loadedPlayer.Base64.disconnect()
}
loadedPlayer.Base64 = io(location.origin,{ path: websocketPath, query: websocketQuery, transports: ['websocket'], forceNew: false})
var ws = loadedPlayer.Base64
var buffer
ws.on('diconnect',function(){
console.log('Base64 Stream Disconnected')
})
ws.on('connect',function(){
ws.emit('Base64',{
auth: $user.auth_token,
uid: $user.uid,
ke: monitor.ke,
id: monitor.mid,
channel: subStreamChannel
})
if(!loadedPlayer.ctx || loadedPlayer.ctx.length === 0){
loadedPlayer.ctx = containerElement.find('canvas');
}
var ctx = loadedPlayer.ctx[0]
var ctx2d = ctx.getContext("2d")
loadedPlayer.image = new Image()
var image = loadedPlayer.image
image.onload = function() {
loadedPlayer.imageLoading = false
var x = 0
var y = 0
ctx.getContext("2d").drawImage(image,x,y,ctx.width,ctx.height)
URL.revokeObjectURL(loadedPlayer.imageUrl)
}
ws.on('data',function(imageData){
try{
if(loadedPlayer.imageLoading === true)return console.log('drop');
loadedPlayer.imageLoading = true
var arrayBufferView = new Uint8Array(imageData);
var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
loadedPlayer.imageUrl = URL.createObjectURL( blob );
loadedPlayer.image.src = loadedPlayer.imageUrl
loadedPlayer.last_frame = 'data:image/jpeg;base64,'+base64ArrayBuffer(imageData)
}catch(er){
debugLog('base64 frame')
}
// $.ccio.init('signal',d);
})
})
break;
case'mp4':
setTimeout(function(){
var stream = containerElement.find('.stream-element');
var onPoseidonError = function(){
// setTimeout(function(){
// mainSocket.f({f:'monitor',ff:'watch_on',id:monitor.mid})
// },5000)
}
if(!loadedPlayer.PoseidonErrorCount)loadedPlayer.PoseidonErrorCount = 0
if(loadedPlayer.PoseidonErrorCount >= 5)return
if(subStreamChannel ? details.substream.output.stream_flv_type === 'ws' : monitor.details.stream_flv_type === 'ws'){
if(loadedPlayer.Poseidon){
loadedPlayer.Poseidon.stop()
revokeVideoPlayerUrl(monitorId)
}
try{
loadedPlayer.Poseidon = new Poseidon({
video: stream[0],
auth_token: $user.auth_token,
ke: monitor.ke,
uid: $user.uid,
id: monitor.mid,
url: location.origin,
path: websocketPath,
query: websocketQuery,
onError : onPoseidonError,
channel : subStreamChannel
})
loadedPlayer.Poseidon.start();
}catch(err){
// onPoseidonError()
console.log('onTryPoseidonError',err)
}
}else{
stream.attr('src',getApiPrefix(`mp4`)+'/'+monitor.mid + (subStreamChannel ? `/${subStreamChannel}` : '')+'/s.mp4?time=' + (new Date()).getTime())
stream[0].onerror = function(err){
console.error(err)
}
}
},1000)
break;
case'flv':
if (flvjs.isSupported()) {
if(loadedPlayer.flv){
loadedPlayer.flv.destroy()
revokeVideoPlayerUrl(monitorId)
}
var options = {};
if(monitor.details.stream_flv_type==='ws'){
if(monitor.details.stream_flv_maxLatency&&monitor.details.stream_flv_maxLatency!==''){
monitor.details.stream_flv_maxLatency = parseInt(monitor.details.stream_flv_maxLatency)
}else{
monitor.details.stream_flv_maxLatency = 20000;
}
options = {
type: 'flv',
isLive: true,
auth_token: $user.auth_token,
ke: monitor.ke,
uid: $user.uid,
id: monitor.mid,
maxLatency: monitor.details.stream_flv_maxLatency,
hasAudio:false,
url: location.origin,
path: websocketPath,
channel : subStreamChannel,
query: websocketQuery
}
}else{
options = {
type: 'flv',
isLive: true,
url: getApiPrefix(`flv`)+'/'+monitor.mid + (subStreamChannel ? `/${subStreamChannel}` : '')+'/s.flv'
}
}
loadedPlayer.flv = flvjs.createPlayer(options);
loadedPlayer.flv.attachMediaElement(containerElement.find('.stream-element')[0]);
loadedPlayer.flv.on('error',function(err){
console.log(err)
});
loadedPlayer.flv.load();
loadedPlayer.flv.play();
}else{
new PNotify({title:'Stream cannot be started',text:'FLV.js is not supported on this browser. Try another stream type.',type:'error'});
}
break;
case'hls':
function createSteamNow(){
clearTimeout(loadedPlayer.m3uCheck)
var url = getApiPrefix(`hls`) + '/' + monitor.mid + (subStreamChannel ? `/${subStreamChannel}` : '') + '/s.m3u8'
$.get(url,function(m3u){
if(m3u == 'File Not Found'){
loadedPlayer.m3uCheck = setTimeout(function(){
createSteamNow()
},2000)
}else{
var video = containerElement.find('.stream-element')[0]
if (isAppleDevice) {
video.src = url;
video.addEventListener('loadedmetadata', function() {
setTimeout(function(){
video.play();
},3000)
}, false);
}else{
var hlsOptions = safeJsonParse(dashboardOptions().hlsOptions) || {}
if(hlsOptions instanceof String){
hlsOptions = {}
new PNotify({
title: lang['Invalid JSON'],
text: lang.hlsOptionsInvalid,
type: `warning`,
})
}
if(loadedPlayer.hls){
loadedPlayer.hls.destroy()
revokeVideoPlayerUrl(monitorId)
}
loadedPlayer.hls = new Hls(hlsOptions)
loadedPlayer.hls.loadSource(url)
loadedPlayer.hls.attachMedia(video)
loadedPlayer.hls.on(Hls.Events.MANIFEST_PARSED,function() {
if (video.paused) {
video.play();
}
});
}
}
})
}
createSteamNow()
break;
case'mjpeg':
var liveStreamElement = containerElement.find('.stream-element')
var setSource = function(){
liveStreamElement.attr('src',getApiPrefix(`mjpeg`)+'/'+monitorId + (subStreamChannel ? `/${subStreamChannel}` : ''))
liveStreamElement.unbind('ready')
liveStreamElement.ready(function(){
setTimeout(function(){
liveStreamElement.contents().find("body").append('<style>img{width:100%;height:100%}</style>')
},1000)
})
}
setSource()
liveStreamElement.on('error',function(err){
setTimeout(function(){
setSource()
},4000)
})
break;
}
$.each(onLiveStreamInitiateExtensions,function(n,extender){
extender(streamType,monitor,loadedPlayer,subStreamChannel)
})
var monitorMutes = dashboardOptions().monitorMutes || {}
if(dashboardOptions().switches.monitorMuteAudio === 1){
containerElement.find('video').each(function(n,el){
el.muted = "muted"
})
}else{
$.each(loadedMonitors,function(frontId,monitor){
setTimeout(() => {
var monitorId = monitor.mid
var muted = monitorMutes[monitorId]
try{
var vidEl = $('.monitor_item[mid="' + monitorId + '"] video')[0]
if(vidEl.length === 0)return;
if(muted === 1){
vidEl.muted = true
}else{
try{
vidEl.muted = false
}catch(err){
console.error('User must have window active to unmute.')
}
}
}catch(err){
// console.log(err)
}
},2000)
})
}
//initiate signal check
if(streamType !== 'useSubstream'){
var signalCheckInterval = (isNaN(loadedMonitor.details.signal_check) ? 10 : parseFloat(loadedMonitor.details.signal_check)) * 1000 * 60
if(signalCheckInterval > 0){
clearInterval(loadedPlayer.signal)
loadedPlayer.signal = setInterval(function(){
signalCheckLiveStream({
mid: monitorId,
checkSpeed: 1000,
})
},signalCheckInterval);
}
}
}
function revokeVideoPlayerUrl(monitorId){
try{
URL.revokeObjectURL(liveGridElements[monitorId].streamElement[0].src)
}catch(err){
debugLog(err)
}
}
function closeLiveGridPlayer(monitorId,killElement){
try{
var livePlayerElement = loadedLiveGrids[monitorId]
if(livePlayerElement){
if(livePlayerElement.hls){livePlayerElement.hls.destroy()}
if(livePlayerElement.Poseidon){livePlayerElement.Poseidon.stop()}
if(livePlayerElement.Base64){livePlayerElement.Base64.disconnect()}
if(livePlayerElement.dash){livePlayerElement.dash.reset()}
if(livePlayerElement.jpegInterval){
stopJpegStream(monitorId)
}
$.each(onLiveStreamCloseExtensions,function(n,extender){
extender(livePlayerElement)
})
}
if(liveGridElements[monitorId])revokeVideoPlayerUrl(monitorId)
clearInterval(livePlayerElement.signal)
}catch(err){
console.log(err)
}
if(killElement){
var theElement = $('#monitor_live_'+monitorId)
if(theElement.length > 0){
theElement.remove()
delete(loadedLiveGrids[monitorId])
delete(liveGridElements[monitorId])
}
}
}
function fullScreenLiveGridStream(monitorItem){
var videoElement = monitorItem.find('.stream-element')
monitorItem.addClass('fullscreen')
if(videoElement.is('canvas')){
var theBody = $('body')
videoElement.attr('height',theBody.height())
videoElement.attr('width',theBody.width())
}
fullScreenInit(videoElement[0])
}
function toggleJpegMode(){
var sendData = {
f: 'monitor',
ff: 'jpeg_on'
}
if(window.jpegModeOn === true){
sendData.ff = 'jpeg_off'
}
mainSocket.f(sendData)
}
function startJpegStream(monitorId){
if(loadedLiveGrids[monitorId]){
var monitor = loadedMonitors[monitorId]
var loadedBlock = loadedLiveGrids[monitorId]
var jpegInterval = !isNaN(monitor.details.jpegInterval) ? parseFloat(monitor.details.jpegInterval) : 1
resetMonitorCanvas(monitorId,false)
var streamElement = $('#monitor_live_' + monitorId + ' .stream-element');
// stopJpegStream(monitorId)
var jpegUrl = getApiPrefix('jpeg') + '/' + monitorId + '/s.jpg?time='
function drawNewFrame(){
streamElement.attr('src',jpegUrl + (new Date()).getTime())
}
streamElement.on('load',function(){
loadedBlock.jpegInterval = setTimeout(drawNewFrame,1000/jpegInterval)
}).on('error',function(){
loadedBlock.jpegInterval = setTimeout(drawNewFrame,1000/jpegInterval)
})
drawNewFrame()
}
}
function stopJpegStream(monitorId){
var livePlayerElement = loadedLiveGrids[monitorId]
if(!livePlayerElement)return;
try{
liveGridElements[monitorId].streamElement.off('load').off('error')
clearTimeout(livePlayerElement.jpegInterval)
}catch(err){
console.log(err)
console.log(monitorId)
}
}
function startAllJpegStreams(monitorId){
$.each(loadedMonitors,function(n,monitor){
startJpegStream(monitor.mid)
})
}
function stopAllJpegStreams(monitorId){
$.each(loadedMonitors,function(n,monitor){
stopJpegStream(monitor.mid)
})
}
function signalCheckLiveStream(options){
try{
var monitorId = options.mid
var monitorConfig = loadedMonitors[monitorId]
var liveGridData = liveGridElements[monitorId]
var monitorItem = liveGridData.monitorItem
var monitorDetails = monitorConfig.details
var checkCount = 0
var base64Data = null;
var checkSpeed = options.checkSpeed || 1000
var subStreamChannel = monitorConfig.subStreamChannel
var streamType = subStreamChannel ? monitorDetails.substream ? monitorDetails.substream.output.stream_type : 'hls' : monitorDetails.stream_type
function failedStreamCheck(){
if(monitorConfig.signal_check_log == 1){
logWriterDraw('[mid="'+monitorId+'"]',{
log: {
type: 'Stream Check',
msg: lang.clientStreamFailedattemptingReconnect
}
})
}
mainSocket.f({f:'monitor',ff:'watch_on',id:monitorId});
}
function succeededStreamCheck(){
if(monitorConfig.signal_check_log == 1){
logWriterDraw('[mid="'+monitorId+'"]',{
log: {
type: 'Stream Check',
msg : lang.Success
}
})
}
}
function executeCheck(){
switch(streamType){
case'b64':
monitorItem.resize()
break;
case'hls':case'flv':case'mp4':
if(monitorItem.find('video')[0].paused){
failedStreamCheck()
}else{
succeededStreamCheck()
}
break;
default:
if(dashboardOptions().jpeg_on === true){return}
getSnapshot({
monitor: loadedMonitors[monitorId],
},function(url){
base64Data = url;
setTimeout(function(){
getSnapshot({
monitor: loadedMonitors[monitorId],
},function(url){
if(base64Data === url){
if(checkCount < 3){
++checkCount;
setTimeout(function(){
executeCheck();
},checkSpeed)
}else{
failedStreamCheck()
}
}else{
succeededStreamCheck()
}
});
},checkSpeed)
});
break;
}
$.each(onSignalCheckLiveStreamExtensions,function(n,extender){
extender(streamType,monitorItem)
})
}
executeCheck();
}catch(err){
console.log(err)
var errorStack = err.stack;
function phraseFoundInErrorStack(x){return errorStack.indexOf(x) > -1}
if(phraseFoundInErrorStack("The HTMLImageElement provided is in the 'broken' state.")){
mainSocket.f({f:'monitor',ff:'watch_on',id:monitorId});
}
clearInterval(liveGridData.signal);
delete(liveGridData.signal);
}
}
function requestMonitorInit(){
mainSocket.f({
f: 'monitor',
ff: 'watch_on',
id: monitorId
});
}
$(document).ready(function(e){
$('body')
.on('dblclick','.stream-block',function(){
var monitorItem = $(this).parents('[data-mid]');
fullScreenLiveGridStream(monitorItem)
})
.on('click','.launch-live-grid-monitor',function(){
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
// if(isMobile){
// createLivePlayerTab(loadedMonitors[monitorId])
// }else{
mainSocket.f({
f: 'monitor',
ff: 'watch_on',
id: monitorId
})
openLiveGrid()
// }
})
.on('click','.reconnect-live-grid-monitor',function(){
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
mainSocket.f({
f: 'monitor',
ff: 'watch_on',
id: monitorId
})
})
.on('click','.toggle-live-grid-monitor-fullscreen',function(){
var monitorItem = $(this).parents('[data-mid]')
fullScreenLiveGridStream(monitorItem)
});
onWebSocketEvent(function (d){
switch(d.f){
case'init_success':
// loadPreviouslyOpenedLiveGridBlocks()
break;
case'video_build_success':
d.status = 1
d.mid = d.id || d.mid
var monitorId = d.mid
var videoTime = d.time
loadedVideosInMemory[`${monitorId}${videoTime}`] = d
break;
case'monitor_watch_off':case'monitor_stopping':
var monitorId = d.mid || d.id
closeLiveGridPlayer(monitorId,(d.f === 'monitor_watch_off'))
break;
case'monitor_status':
if(
tabTree.name === 'liveGrid' &&
(
d.code === 2 ||
d.code === 3
)
){
var monitorId = d.mid || d.id
setTimeout(function(){
callMonitorToLiveGrid(loadedMonitors[monitorId])
},2000)
}
break;
case'substream_start':
loadedMonitors[d.mid].subStreamChannel = d.channel
setTimeout(() => {
resetMonitorCanvas(d.mid,true,d.channel)
},3000)
break;
case'substream_end':
loadedMonitors[d.mid].subStreamChannel = null
resetMonitorCanvas(d.mid,true,null)
break;
case'monitor_watch_on':
var monitorId = d.mid || d.id
var loadedMonitor = loadedMonitors[monitorId]
var subStreamChannel = d.subStreamChannel
if(!loadedMonitor.subStreamChannel && loadedMonitor.details.stream_type === 'useSubstream'){
toggleSubStream(monitorId,function(){
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel)
})
}else{
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel)
}
break;
case'mode_jpeg_off':
window.jpegModeOn = false
$.each(loadedMonitors,function(n,v){
stopJpegStream(v.mid)
resetMonitorCanvas(v.mid)
initiateLiveGridPlayer(v)
})
$('body').removeClass('jpegMode')
break;
case'mode_jpeg_on':
window.jpegModeOn = true
startAllJpegStreams()
$('body').addClass('jpegMode')
break;
case'detector_trigger':
var monitorId = d.id
var liveGridElement = liveGridElements[monitorId]
if(!window.dontShowDetection && liveGridElement){
var monitorElement = liveGridElement.monitorItem
var livePlayerElement = loadedLiveGrids[monitorId]
if(d.doObjectDetection === true){
monitorElement.addClass('doObjectDetection')
clearTimeout(livePlayerElement.detector_trigger_doObjectDetection_timeout)
livePlayerElement.detector_trigger_doObjectDetection_timeout = setTimeout(function(){
monitorElement.removeClass('doObjectDetection')
},3000)
}else{
monitorElement.removeClass('doObjectDetection')
}
if(d.details.matrices&&d.details.matrices.length>0){
drawMatrices(d,{
theContainer: liveGridElement.eventObjects,
height: liveGridElement.height,
width: liveGridElement.width,
})
}
if(d.details.confidence){
var eventConfidence = d.details.confidence
if(eventConfidence > 100)eventConfidence = 100
liveGridElement.motionMeter.css('width',eventConfidence + '%');
liveGridElement.motionMeterText[0].innerHtml = d.details.confidence+'% change in <b>'+d.details.name+'</b>'
}
monitorElement.addClass('detector_triggered')
clearTimeout(livePlayerElement.detector_trigger_timeout);
livePlayerElement.detector_trigger_timeout = setTimeout(function(){
monitorElement.removeClass('detector_triggered');
liveGridElement.eventObjects.find('.stream-detected-object,.stream-detected-point').remove()
},800);
playAudioAlert()
var monitorPop = monitorPops[monitorId]
if($user.details.event_mon_pop === '1' && (!monitorPop || monitorPop.closed === true)){
popOutMonitor(monitorId)
}
// console.log({
// ke: d.ke,
// mid: monitorId,
// log: {
// type: lang['Event Occurred'],
// msg: d.details,
// }
// })
}
break;
}
});
createWebsocket(`ws://${location.host}`,'/socket.io');
onInitWebsocket(function(){
requestMonitorInit();
});
});

View File

@ -0,0 +1,59 @@
var isAppleDevice = navigator.userAgent.match(/(iPod|iPhone|iPad)/)||(navigator.userAgent.match(/(Safari)/)&&!navigator.userAgent.match('Chrome'));
var isMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))
function safeJsonParse(string){
if(string instanceof Object){
return string
}else{
}
var newObject = {}
try{
newObject = JSON.parse(string)
}catch(err){
}
return newObject
}
function checkCorrectPathEnding(x){
var length=x.length
if(x.charAt(length-1)!=='/'){
x=x+'/'
}
return x
}
function getLocation(d){
var url
if(d && d.info && d.info.URL){
url = d.info.URL
if(url.charAt(url.length-1) !== '/'){
url = url+'/'
}
}else{
url = urlPrefix
}
return url
}
function getApiPrefix(path){
var mainPart = getLocation() + $user.auth_token
return path ? mainPart + '/' + path + '/' + $user.ke : mainPart
}
function dashboardOptions(r,rr,rrr){
if(!rrr){rrr={};};if(typeof rrr === 'string'){rrr={n:rrr}};if(!rrr.n){rrr.n='ShinobiOptions_'+location.host}
ii={o:localStorage.getItem(rrr.n)};try{ii.o=JSON.parse(ii.o)}catch(e){ii.o={}}
if(!ii.o){ii.o={}}
if(r&&rr&&!rrr.x){
ii.o[r]=rr;
}
switch(rrr.x){
case 0:
delete(ii.o[r])
break;
case 1:
delete(ii.o[r][rr])
break;
}
localStorage.setItem(rrr.n,JSON.stringify(ii.o))
return ii.o
}

View File

@ -5,4 +5,7 @@ onWebSocketEvent(function (d){
setInterfaceCounts()
break;
}
})
});
$(document).ready(function(){
createWebsocket();
});

View File

@ -15,9 +15,9 @@ function onWebSocketEvent(theAction){
onWebSocketEventFunctions.push(theAction)
}
var queuedCallbacks = {}
$(document).ready(function(){
mainSocket = io(location.origin,{
path: websocketPath,
function createWebsocket(theURL,thePath){
mainSocket = io(theURL || location.origin,{
path: thePath || websocketPath,
query: websocketQuery
})
mainSocket.f = function(data,callback){
@ -73,4 +73,4 @@ $(document).ready(function(){
theAction(d)
})
})
})
}

View File

@ -1,424 +1,67 @@
<script>var io=null;</script>
<%
data.name='SHINOBI_'+data.ke+'_'+data.id;
if(config.ssl&&config.ssl.port&&data.protocol==='https'){
data.port=config.ssl.port
}else{
data.port=config.port
}
if(!data.port||data.port===''||data.port==80||data.port==443){data.url=baseUrl}else{data.url=baseUrl+':'+data.port}
if(data.addon || data.addon.indexOf('relative')>-1){
data.url = ''
}else if(config.baseURL){
data.url = config.baseURL
}
if(data.url.charAt(data.url.length - 1) !== '/'){
data.url += '/'
}
var urlPrefix = ``
var targetPort = config.ssl && config.ssl.port && data.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(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 += '/'
}
%>
<script src="<%=data.url%>assets/vendor/js/socket.io.min.js"></script>
<script src="<%=data.url%>assets/vendor/js/poseidon.js"></script>
<script src="<%=data.url%>assets/vendor/js/hls.min.js"></script>
<script src="<%=data.url%>assets/vendor/js/flv.min.js"></script>
<% if(data.addon){
var ar={}
decodeURI(data.addon).split('|').forEach(function(v){
if(v.indexOf('=')>-1){
v=v.split('=');
ar[v[0]]=v[1];
}
})
if(!ar.width){ar.width=640;}
if(!ar.height){ar.height=480;}
if(data.addon.indexOf('jquery')>-1){ %>
<script src="<%=data.url%>assets/vendor/js/jquery.min.js"></script>
<% };
if(data.addon.indexOf('gui')>-1){ %>
<style>
body {position:relative;}
.shinobi_stream{position:absolute;width:100%;height:100%;top:0;left:0;}
.shinobi_hud{position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;transition:0.2s}
.shinobi_hud:hover{opacity:1}
.shinobi_hud .shinobi_viewers{position:absolute;top:5px;left:5px;}
.shinobi_hud .shinobi_viewers{
display: inline-block;
min-width: 10px;
padding: 3px 7px;
font-size: 12px;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: middle;
background-color: #777;
border-radius: 10px;
font-family: sans-serif;
}
iframe.stream-element{border:0;}
/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */
.shinobi_ws_http_toggle {
width: 50px;
height: 20px;
position: absolute;
bottom: 10px;
left: 10px;
}
.shinobi_ws_http_toggle input[type=checkbox]{
height: 0;
width: 0;
visibility: hidden;
}
.shinobi_ws_http_toggle label {
cursor: pointer;
text-indent: -9999px;
width: 100px;
height: 20px;
background: grey;
display: block;
border-radius: 100px;
position: relative;
}
.shinobi_ws_http_toggle label:after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
background: #fff;
border-radius: 90px;
transition: 0.3s;
}
.shinobi_ws_http_toggle input:checked + label {
background: #00118c;
}
.shinobi_ws_http_toggle input:checked + label:after {
left: calc(100% - 5px);
transform: translateX(-100%);
}
.shinobi_ws_http_toggle label:active:after {
width: 10px;
}
</style>
<% };
if(data.addon.indexOf('fullscreen')>-1){ %>
<style>
body,html{overflow: hidden;height:100%}
*{margin:0;padding:0;border:0}
.stream-element,.shinobi_stream{position:absolute;top:0;left:0;height:100%}
.shinobi_stream video{object-fit: fill}
</style>
<script src="<%- urlPrefix %>assets/vendor/js/socket.io.min.js"></script>
<script src="<%- urlPrefix %>assets/vendor/js/poseidon.js"></script>
<script src="<%- urlPrefix %>assets/vendor/js/hls.min.js"></script>
<script src="<%- urlPrefix %>assets/vendor/js/flv.min.js"></script>
<script>
$(window).resize(function(){
$('.stream-element').attr('width',$('body').width())
$('.stream-element').attr('height',$('body').height())
})
var urlPrefix = `<%- urlPrefix %>`;
var $user = <%- JSON.stringify($user) %>;
var definitions = <%- JSON.stringify(define) %>;
var lang = $user.lang;
var hasGUI = <%- hasGUI || false %>;
var streamWidth = <%- streamWidth %>;
var streamHeight = <%- streamHeight %>;
var monitorId = `<%- data.id %>`;
var monitorConfig = <%- JSON.stringify(mon) %>;
var loadedMonitors = {}
loadedMonitors[monitorId] = monitorConfig;
</script>
<% if(getAddon('jquery')){ %>
<script src="<%- urlPrefix %>assets/vendor/js/jquery.min.js"></script>
<% };
if(hasGUI){ %>
<link rel="stylesheet" href="<%- urlPrefix %>assets/css/bs5.embed.gui.css">
<link rel="stylesheet" href="<%- urlPrefix %>assets/css/bs5.embed.detections.css">
<% };
if(isFullscreen){ %>
<link rel="stylesheet" href="<%- urlPrefix %>assets/css/bs5.embed.fullscreen.css">
<script src="<%- urlPrefix %>assets/js/bs5.embed.fullscreen.js"></script>
<% } %>
<% }else{
//no addon set, do defaults
var ar={};
ar.width=640;
ar.height=480;
} %>
<script>
$(document).ready(function(){
$('#<%= data.name %> canvas').attr('width',<%=ar.width%>).attr('height',<%=ar.height%>)
})
</script>
<div class="shinobi_stream" id="<%= data.name %>">
<% switch(mon.details.stream_type){
case'jpeg':
%><img class="stream-element"><%
break;
case'flv':case'hls':case'mp4':
%><video class="stream-element" autoplay></video><%
break;
case'mjpeg':
%><iframe class="stream-element"></iframe><%
break;
default:
%><canvas class="stream-element"></canvas><%
break;
} %>
<% if(data.addon&&data.addon.indexOf('gui')>-1){ %>
<div class="shinobi_hud">
<div class="shinobi_viewers" title="Current number of viewers"></div>
<div class="shinobi_ws_http_toggle" style="display:none">
<input type="checkbox" id="shinobi_ws_http_toggle" <% if(mon.details.stream_flv_type === 'ws'){ %>checked<% } %> /><label for="shinobi_ws_http_toggle">WebSocket</label>
</div>
</div>
<% } %>
<div class="shinobi_stream" id="monitors_live">
<div class="stream-element-container"></div>
</div>
<script>
var SHINOBI_TIMER=setInterval(function(){
if(io){
clearInterval(SHINOBI_TIMER);delete(SHINOBI_TIMER);
if(!$.shinobi){
$.shinobi={}
};
if(!$.shinobi.mon){
$.shinobi.mon={}
};
$.shinobi.init=function(d){
if($.shinobi.mon[d.id].Base64 && $.shinobi.mon[d.id].Base64.connected){
$.shinobi.mon[d.id].Base64.disconnect()
}
if($.shinobi.mon[d.id].Poseidon){
$.shinobi.mon[d.id].Poseidon.stop()
}
if($.shinobi.mon[d.id].flv){
$.shinobi.mon[d.id].flv.destroy()
}
if($.shinobi.mon[d.id].hls){
$.shinobi.mon[d.id].hls.destroy()
}
clearInterval($.shinobi.mon[d.id].jpegInterval);
switch($.shinobi.mon[d.id].details.stream_type){
case'b64':
$.shinobi.mon[d.id].Base64 = io('<%=data.url%>',{transports: ['websocket'], forceNew: false})
var ws = $.shinobi.mon[d.id].Base64
ws.on('diconnect',function(){
console.log('Base64 Stream Disconnected')
})
ws.on('connect',function(){
ws.emit('Base64',{
auth:'<%=data.auth%>',
ke:d.ke,
uid:'<%=data.uid%>',
id:d.id,
url: '<%=data.url%>'
})
if(!$.shinobi.mon[d.id].ctx||$.shinobi.mon[d.id].ctx.length===0){
$.shinobi.mon[d.id].ctx = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')
}
var ctx = $.shinobi.mon[d.id].ctx[0]
$.shinobi.mon[d.id].image = new Image()
var image = $.shinobi.mon[d.id].image
image.onload = function() {
$.shinobi.mon[d.id].imageLoading = false
d.x = 0
d.y = 0
// d.ratio = Math.min(ctx.width/image.width,ctx.height/image.height)
// d.height = image.height * d.ratio
// d.width = image.width * d.ratio
// if(d.width < ctx.width){
// d.x = (ctx.width / 2) - (d.width / 2)
// }
// if(d.height < ctx.height){
// d.y = (ctx.height / 2) - (d.height / 2)
// }
// ctx.getContext("2d").drawImage(image,d.x,d.y,d.width,d.height)
ctx.getContext("2d").drawImage(image,d.x,d.y,ctx.width,ctx.height)
URL.revokeObjectURL($.shinobi.mon[d.id].imageUrl)
}
ws.on('data',function(imageData){
try{
if($.shinobi.mon[d.id].imageLoading === true)return console.log('drop');
// var base64Frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData)
$.shinobi.mon[d.id].imageLoading = true
// $.shinobi.mon[d.id].image.src = base64Frame
var arrayBufferView = new Uint8Array(imageData);
var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
$.shinobi.mon[d.id].imageUrl = URL.createObjectURL( blob );
$.shinobi.mon[d.id].image.src = $.shinobi.mon[d.id].imageUrl
$.shinobi.mon[d.id].last_frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData)
}catch(er){
console.log(er)
}
})
})
break;
case'mp4':
var stream = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element');
if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){
var createPoseidon = function(){
var onPoseidonError = function(){
// setTimeout(function(){
// $.ccio.cx({f:'monitor',ff:'watch_on',id:d.id},user)
// },2000)
}
$.shinobi.mon[d.id].Poseidon = new Poseidon({
video: stream[0],
auth_token:'<%=data.auth%>',
ke:d.ke,
uid:'<%=data.uid%>',
id:d.id,
url: '<%=data.url%>',
onError : onPoseidonError
});
$.shinobi.mon[d.id].Poseidon.start();
$.shinobi.mon[d.id].Poseidon._socket.on('data',function(res){
console.log(res)
})
}
try{
createPoseidon()
}catch(err){
console.log(err)
setTimeout(function(){
createPoseidon()
},3000)
}
}else{
stream.attr('src','<%=data.url%><%=data.auth%>/mp4/'+d.ke+'/'+d.id+'/s.mp4')
}
break;
case'flv':
if (flvjs.isSupported()) {
var options = {};
// if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){
// if($.shinobi.mon[d.id].details.stream_flv_maxLatency&&$.shinobi.mon[d.id].details.stream_flv_maxLatency!==''){
// $.shinobi.mon[d.id].details.stream_flv_maxLatency = parseInt($.shinobi.mon[d.id].details.stream_flv_maxLatency)
// }else{
// $.shinobi.mon[d.id].details.stream_flv_maxLatency = 20000;
// }
// options = {
// type: 'flv',
// isLive: true,
// auth_token:'<%=data.auth%>',
// ke:d.ke,
// uid:'<%=data.uid%>',
// id:d.id,
// maxLatency:$.shinobi.mon[d.id].details.stream_flv_maxLatency,
// hasAudio:false,
// url: '<%=data.url%>'
// }
// }else{
options = {
type: 'flv',
isLive: true,
url: '<%=data.url%><%=data.auth%>/flv/'+d.ke+'/'+d.id+'/s.flv'
}
// }
$.shinobi.mon[d.id].flv = flvjs.createPlayer(options);
$.shinobi.mon[d.id].flv.attachMediaElement($('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0]);
$.shinobi.mon[d.id].flv.on('error',function(err){
console.log(err)
});
$.shinobi.mon[d.id].flv.load();
$.shinobi.mon[d.id].flv.play();
}else{
alert({title:'Stream cannot be started',text:'FLV.js is not supported on this browser. Try another stream type.',type:'error'})
}
break;
case'jpeg':
d.mon=$.shinobi.mon[d.id]
k=d.mon.details;
k.jpegInterval=parseFloat(k.jpegInterval);
if(!k.jpegInterval||k.jpegInterval===''||isNaN(k.jpegInterval)){k.jpegInterval=1}
if(!$.shinobi.mon[d.id].jpegInterval){
clearInterval($.shinobi.mon[d.id].jpegInterval);
$.shinobi.mon[d.id].jpegInterval=setInterval(function(){
$('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element').attr('src','<%=data.url%><%=data.auth%>/jpeg/'+d.mon.ke+'/'+d.mon.mid+'/s.jpg?time='+(new Date()).getTime())
},1000/k.jpegInterval);
}
break;
case'hls':
var video = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0];
d.url='<%=data.url%><%=data.auth%>/hls/'+d.ke+'/'+d.id+'/s.m3u8';
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)||(navigator.userAgent.match(/(Safari)/)&&!navigator.userAgent.match('Chrome'))) {
video.src=d.url;
video.play();
}else{
$.shinobi.mon[d.id].hls = new Hls();
$.shinobi.mon[d.id].hls.loadSource(d.url);
$.shinobi.mon[d.id].hls.attachMedia(video);
$.shinobi.mon[d.id].hls.on(Hls.Events.MANIFEST_PARSED,function() {
setTimeout(function(){
video.play();
},1000)
});
}
break;
case'mjpeg':
$('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element').attr('src','<%=data.url%><%=data.auth%>/mjpeg/'+d.ke+'/'+d.id+'?full=true')
break;
}
}
$.shinobi.mon['<%=data.id%>']=<%- JSON.stringify(mon) %>;
if(!$.shinobi.callback){$.shinobi.callback=function(){}}
if(!$.shinobi.ws||$.shinobi.ws.connected===false){
$.shinobi.ws=io('<%=data.url%>');
$.shinobi.ws.on('f',function (d){
if(d.viewers){
$('#SHINOBI_'+d.ke+'_'+d.id+' .shinobi_viewers').html(d.viewers);
}
switch(d.f){
case'monitor_frame':
var image = new Image();
var ctx = $('#SHINOBI_'+d.ke+'_'+d.id+' canvas')[0];
image.onload = function() {
ctx.getContext("2d").drawImage(image,0,0,ctx.width,ctx.height);
delete(d.frame);
delete(image);
};
image.src='data:image/jpeg;base64,'+d.frame
break;
case'monitor_watch_off':case'monitor_watch_on':
$('#SHINOBI_'+d.ke+'_'+d.id+' .shinobi_viewers').html(d.viewers)
$.shinobi.init(d)
break;
case'monitor_edit':
if(!d.id){d.id=d.mon.mid;}
if($.shinobi.mon[d.id]){
clearInterval($.shinobi.mon[d.id].jpegInterval);
}
d.e=$('#SHINOBI_'+d.ke+'_'+d.id+'');
d.e.find('.stream-element').remove();
d.tmp='';
switch(d.mon.details.stream_type){
case'hls':
d.tmp+='<video class="stream-element" controls autoplay></video>';
break;
case'mjpeg':
d.tmp+='<iframe class="stream-element"></iframe>';
break;
case'jpeg'://base64
d.tmp+='<img class="stream-element">';
break;
default://base64
d.tmp+='<canvas class="stream-element"></canvas>';
break;
}
d.e.append(d.tmp).find('.stream-element').resize();
$(window).resize();
// d.mon.details=JSON.stringify(d.mon.details);
d.mon.id = d.mon.mid
$.shinobi.mon[d.id] = d.mon;
$.shinobi.init(d.mon);
break;
}
$.shinobi.callback()
});
};
$.shinobi.ws.emit('e',{f:'init',auth:'<%=data.auth%>',id:'<%=data.id%>',ke:'<%=data.ke%>'})
$(window).resize();
}
},1000);
//websocket / http toggle
$('#shinobi_ws_http_toggle').change(function(){
var monitor = $.shinobi.mon['<%=data.id%>'];
var parent = $(this).parents('.shinobi_ws_http_toggle')
var label = parent.find('label')
if(monitor.details.stream_flv_type !== 'ws'){
label.text('WebSocket')
monitor.details.stream_flv_type = 'ws'
}else{
label.text('HTTP')
monitor.details.stream_flv_type = 'http'
}
monitor.id = monitor.mid
$.shinobi.init(monitor);
})
$('.shinobi_ws_http_toggle').show()
</script>
<script src="<%- urlPrefix %>assets/js/bs5.embed.utils.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.websocket.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.embed.js"></script>