Shinobi/web/assets/js/bs5.embed.js

649 lines
26 KiB
JavaScript

var loadedLiveGrids = {}
var monitorPops = {}
var liveGridElements = {}
var runningJpegStreams = {}
var liveGrid = $('#monitors_live .stream-element-container')
var websocketPath = checkCorrectPathEnding(urlPrefix) + 'socket.io'
//
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 debugLog(...args){
console.log(...args)
}
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);
console.log(liveGrid.length,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 unmuteVideoPlayer(){
console.log('Unmuting...')
setTimeout(function(){
try{
var videoEl = $(`video`)
if(videoEl.length > 0){
videoEl[0].muted = false;
}
}catch(err){
console.log(err)
}
},3000)
$('.unmute-embed-audio').remove()
}
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 containerElement = $(`#monitor_live_${monitor.mid}`)
var streamType = subStreamChannel ? details.substream ? details.substream.output.stream_type : 'hls' : details.stream_type
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, 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
}catch(er){
console.error('base64 frame',er)
}
})
})
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
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 = {
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)
})
//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
});
}
function toggleSubStream(monitorId,callback){
var monitor = loadedMonitors[monitorId]
var substreamConfig = monitor.details.substream
var isSubStreamConfigured = !!substreamConfig.output;
if(!isSubStreamConfigured){
new PNotify({
type: 'warning',
title: lang['Invalid Settings'],
text: lang.SubstreamNotConfigured,
});
return;
}
if(monitor.subStreamToggleLock)return false;
monitor.subStreamToggleLock = true
$.getJSON(getApiPrefix() + '/toggleSubstream/'+$user.ke+'/'+monitorId,function(d){
monitor.subStreamToggleLock = false
debugLog(d)
if(callback)callback()
})
}
$(document).ready(function(e){
$('body')
.on('dblclick','.stream-block',function(){
var monitorItem = $(this).parents('[data-mid]');
fullScreenLiveGridStream(monitorItem)
})
.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'monitor_watch_off':case'monitor_stopping':
var monitorId = d.mid || d.id
closeLiveGridPlayer(monitorId,(d.f === 'monitor_watch_off'))
break;
case'monitor_status':
if(monitorId === d.mid && d.code === 2 || d.code === 3){
setTimeout(function(){
requestMonitorInit()
},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':
console.log(d)
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);
}
break;
}
});
createWebsocket(location.origin,{
path: websocketPath
});
onInitWebsocket(function(){
requestMonitorInit();
});
$(window)
.focus(function(){
unmuteVideoPlayer()
})
$('.unmute-embed-audio').click(function(){
unmuteVideoPlayer()
})
});