var availableMonitorGroups = {} var monitorGroupSelections = $('#monitor-group-selections') var onGetSnapshotByStreamExtensions = [] function onGetSnapshotByStream(callback){ onGetSnapshotByStreamExtensions.push(callback) } var onBuildStreamUrlExtensions = [] function onBuildStreamUrl(callback){ onBuildStreamUrlExtensions.push(callback) } function humanReadableModeLabel(mode){ var humanMode = lang['Disabled'] switch(mode){ case'idle': humanMode = lang['Idle'] break; case'stop': humanMode = lang['Disabled'] break; case'record': humanMode = lang['Record'] break; case'start': humanMode = lang['Watch Only'] break; } return humanMode } function setCosmeticMonitorInfo(monitorConfig){ var monitorId = monitorConfig.mid var monitorElements = $('.glM' + monitorId) if(safeJsonParse(monitorConfig.details).vcodec !=='copy' && monitorConfig.mode == 'record'){ monitorElements.find('.monitor_not_record_copy').show() }else{ monitorElements.find('.monitor_not_record_copy').hide() } var humanReadableMode = humanReadableModeLabel(monitorConfig.mode) monitorElements.find('.monitor_name').text(monitorConfig.name) monitorElements.find('.monitor_mid').text(monitorId) monitorElements.find('.monitor_ext').text(monitorConfig.ext) monitorElements.find('.monitor_mode').text(humanReadableMode) monitorElements.find('.monitor_status').html(definitions['Monitor Status Codes'][monitorConfig.code] || monitorConfig.status || '') monitorElements.attr('mode',humanReadableMode) monitorElements.find('.lamp').attr('title',humanReadableMode) if(monitorConfig.details.control=="1"){ monitorElements.find('[monitor="control_toggle"]').show() }else{ monitorElements.find('.pad').remove() monitorElements.find('[monitor="control_toggle"]').hide() } } function getSnapshot(options,cb){ var image_data var url var monitor = options.mon || options.monitor || options var targetElement = $(options.targetElement || `[data-mid="${monitor.mid}"].monitor_item .stream-element`) var details = safeJsonParse(monitor.details) var streamType = details.stream_type; if(window.jpegModeOn !== true){ function completeAction(image_data,width,height){ var len = image_data.length var arraybuffer = new Uint8Array( len ) for (var i = 0; i < len; i++) { arraybuffer[i] = image_data.charCodeAt(i) } try { var blob = new Blob([arraybuffer], {type: 'application/octet-stream'}) } catch (e) { var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder) bb.append(arraybuffer); var blob = bb.getBlob('application/octet-stream'); } url = (window.URL || window.webkitURL).createObjectURL(blob) cb(url,image_data,width,height) try{ setTimeout(function(){ URL.revokeObjectURL(url) },10000) }catch(er){} } switch(streamType){ case'hls': case'flv': case'mp4': getVideoSnapshot(targetElement[0],function(base64,video_data,width,height){ completeAction(video_data,width,height) }) break; case'mjpeg': $('#temp').html('') var c = $('#temp canvas')[0] var img = $('img',targetElement.contents())[0] c.width = img.width c.height = img.height var ctx = c.getContext('2d') ctx.drawImage(img, 0, 0,c.width,c.height) completeAction(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height) break; case'b64': var c = targetElement[0] var ctx = c.getContext('2d') completeAction(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height) break; case'jpeg': url = targetElement.attr('src') image_data = new Image() image_data.src = url cb(url,image_data,image_data.width,image_data.height) break; } $.each(onGetSnapshotByStreamExtensions,function(n,extender){ extender(streamType,targetElement,completeAction,cb) }) }else{ url = targetElement.attr('src') image_data = new Image() image_data.src = url cb(url,image_data,image_data.width,image_data.height) } } function getVideoSnapshot(videoElement,cb){ var image_data var base64 $('#temp').html('') var c = $('#temp canvas')[0] var img = videoElement c.width = img.videoWidth c.height = img.videoHeight var ctx = c.getContext('2d') ctx.drawImage(img, 0, 0,c.width,c.height) base64=c.toDataURL('image/jpeg') image_data=atob(base64.split(',')[1]) var arraybuffer = new ArrayBuffer(image_data.length) var view = new Uint8Array(arraybuffer) for (var i=0; i-1){ row[m]=b; } }) return row } function downloadMonitorConfigurationsToDisk(monitorIds){ var selectedMonitors = [] $.each(monitorIds,function(n,monitorId){ var monitor = monitorId instanceof Object ? monitorId : loadedMonitors[monitorId] if(monitor)selectedMonitors.push(monitor) }) var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(selectedMonitors)); $('#temp').html('') .find('a') .attr('href',dataStr) .attr('download',`${applicationName}_Monitors_${$user.ke}_${new Date()}.json`) [0].click() } function importShinobiMonitor(monitor){ } function importM3u8Playlist(textData){ var m3u8List = textData.replace('#EXTM3U','').trim().split('\n') var parsedList = {} var currentName m3u8List.forEach(function(line){ if(line.indexOf('#EXTINF:-1,') > -1){ currentName = line.replace('#EXTINF:-1,','').trim() }else{ parsedList[currentName] = line.trim() } }) $.each(parsedList,function(name,url){ var link = getUrlPieces(url) var newMon = generateDefaultMonitorSettings() newMon.details = JSON.parse(newMon.details) newMon.mid = 'HLS' + name.toLowerCase() newMon.name = name newMon.port = link.port newMon.host = link.hostname newMon.path = link.pathname newMon.details.tv_channel = '1' newMon.details.tv_channel_id = name newMon.details.auto_host_enable = '1' newMon.details.auto_host = url newMon.details.stream_quality = '1' newMon.details.stream_fps = '' newMon.details.stream_vcodec = 'libx264' newMon.details.stream_acodec = 'aac' newMon.details.stream_type = 'hls' newMon.details.hls_time = '10' newMon.type = 'mp4' newMon.details = JSON.stringify(newMon.details) postMonitor(newMon) }) } function importZoneMinderMonitor(Monitor){ var newMon = generateDefaultMonitorSettings() newMon.details = JSON.parse(newMon.details) newMon.details.stream_type = 'jpeg' switch(Monitor.Type.toLowerCase()){ case'ffmpeg':case'libvlc': newMon.details.auto_host_enable = '1' newMon.details.auto_host = Monitor.Path if(newMon.auto_host.indexOf('rtsp://') > -1 || newMon.auto_host.indexOf('rtmp://') > -1 || newMon.auto_host.indexOf('rtmps://') > -1){ newMon.type = 'h264' }else{ $.ccio.init('note',{title:lang['Please Check Your Settings'],text:lang.migrateText1,type:'error'}) } break; case'local': newMon.details.auto_host = Monitor.Device break; case'remote': break; } newMon.details = JSON.stringify(newMon.details) return newMon } function importMonitor(textData){ try{ var parsedData = textData instanceof Object ? textData : safeJsonParse(textData) function postMonitor(v){ var monitorId = v.mid $.post(`${getApiPrefix('configureMonitor')}/${monitorId}`,{ data: JSON.stringify(v,null,3) },function(d){ debugLog(d) }) } //zoneminder one monitor if(parsedData.monitor){ postMonitor(importZoneMinderMonitor(parsedData.monitor.Monitor)) }else //zoneminder multiple monitors if(parsedData.monitors){ $.each(parsedData.monitors,function(n,v){ postMonitor(importZoneMinderMonitor(v.Monitor)) }) }else //shinobi one monitor if(parsedData.mid){ postMonitor(parsedData) }else //shinobi multiple monitors if(parsedData[0] && parsedData[0].mid){ $.each(parsedData,function(n,v){ postMonitor(v) }) } }catch(err){ //#EXTM3U if(textData instanceof String && textData.indexOf('#EXTM3U') > -1 && textData.indexOf('{"') === -1){ importM3u8Playlist(textData) }else{ debugLog(err) new PNotify({ title: lang['Invalid JSON'], text: lang.InvalidJSONText, type: 'error' }) } } } function deleteMonitors(monitorsSelected,afterDelete){ $.confirm.create({ title: lang['Delete']+' '+lang['Monitors'], body: '

'+lang.DeleteMonitorsText+'

', clickOptions: [ { title:lang['Delete']+' '+lang['Monitors'], class:'btn-danger', callback:function(){ $.each(monitorsSelected,function(n,monitor){ $.getJSON(`${getApiPrefix(`configureMonitor`)}/${monitor.mid}/delete`,function(data){ console.log(data) if(monitorsSelected.length === n + 1){ //last if(afterDelete)afterDelete(monitorsSelected) } }) }) } }, { title:lang['Delete Monitors and Files'], class:'btn-danger', callback:function(){ $.each(monitorsSelected,function(n,monitor){ $.getJSON(`${getApiPrefix(`configureMonitor`)}/${monitor.mid}/delete?deleteFiles=true`,function(data){ console.log(data) if(monitorsSelected.length === n + 1){ //last if(afterDelete)afterDelete(monitorsSelected) } }) }) } } ] }) } function launchImportMonitorWindow(callback){ var html = `${lang.ImportMultiMonitorConfigurationText}
` $.confirm.create({ title: lang['Import Monitor Configuration'], body: html, clickOptions: [ { title: lang['Import'], class: 'btn-primary', callback: function(){ var textData = safeJsonParse($.confirm.e.find('textarea').val()) if(callback){ callback(textData) }else{ importMonitor(textData) } } }, // { // title: lang['Upload'], // class: 'btn-primary', // callback: function(e){ // var files = e.target.files; // FileList object // f = files[0]; // var reader = new FileReader(); // reader.onload = function(ee) { // $.confirm.e.find('textarea').val(ee.target.result); // } // reader.readAsText(f); // } // } ], }) $.confirm.e.find('.upload').change(function(e){ var files = e.target.files; // FileList object f = files[0]; var reader = new FileReader(); reader.onload = function(ee) { $.confirm.e.find('textarea').val(ee.target.result); } reader.readAsText(f); }); } function drawMatrices(event,options){ var theContainer = options.theContainer var height = options.height var width = options.width var widthRatio = width / event.details.imgWidth var heightRatio = height / event.details.imgHeight var objectTagGroup = event.details.reason === 'motion' ? 'motion' : event.details.name theContainer.find(`.stream-detected-object[name="${objectTagGroup}"]`).remove() var html = '' $.each(event.details.matrices,function(n,matrix){ html += `
` if(matrix.tag)html += `${matrix.tag}` html += '
' }) theContainer.append(html) } function setMonitorCountOnUI(){ $('.cameraCount').text(Object.keys(loadedMonitors).length) } function muteMonitorAudio(monitorId,buttonEl){ var masterMute = dashboardOptions().switches.monitorMuteAudio var monitorMutes = dashboardOptions().monitorMutes || {} monitorMutes[monitorId] = monitorMutes[monitorId] === 1 ? 0 : 1 dashboardOptions('monitorMutes',monitorMutes) var vidEl = $('.monitor_item[data-mid="' + monitorId + '"] video')[0] try{ if(monitorMutes[monitorId] === 1){ vidEl.muted = true }else{ if(masterMute !== 1){ if(windowFocus && hadFocus){ vidEl.muted = false }else{ console.error('User must have window active to unmute.') } } } }catch(err){ console.log(err) } var volumeIcon = monitorMutes[monitorId] !== 1 ? 'volume-up' : 'volume-off' if(buttonEl)buttonEl.find('i').removeClass('fa-volume-up fa-volume-off').addClass('fa-' + volumeIcon) } function getAvailableMonitorGroups(){ var theGroups = {} $.each(loadedMonitors,function(n,monitor){ var monitorsGroups = safeJsonParse(monitor.details.groups) $.each(monitorsGroups,function(m,groupId){ if(!theGroups[groupId])theGroups[groupId]={} theGroups[groupId][monitor.mid] = monitor }) }) availableMonitorGroups = theGroups return theGroups; } function drawMonitorGroupList(){ var html = '' getAvailableMonitorGroups() $.each(availableMonitorGroups,function(groupId,v){ if($user.mon_groups[groupId]){ html += `
  • ${$user.mon_groups[groupId].name}
  • ` } }) monitorGroupSelections.html(html) } function loadMonitorGroup(groupId){ $.each(dashboardOptions().watch_on,function(groupKey,groupData){ $.each(groupData,function(monitorId,isOn){ if(isOn)mainSocket.f({ f: 'monitor', ff: 'watch_off', id: monitorId, ke: $user.ke }) }) }) $.each(availableMonitorGroups[groupId],function(n,monitor){ mainSocket.f({ f: 'monitor', ff: 'watch_on', id: monitor.mid, ke: $user.ke }) }) } function buildDefaultMonitorMenuItems(){ return `
  • ${lang['Live Grid']}
  • ${lang['Pop']}
  • ${lang['Toggle Substream']}
  • ${lang['Videos List']}
  • ${lang['Time-lapse']}
  • ${lang['Monitor Settings']}
  • ${lang['Export']}
  • ${lang['Set Mode']}
  • ${lang.Disable}
  • ${lang['Watch-Only']}
  • ${lang.Record}
  • ` } function magnifyStream(options){ if(!options.p && !options.parent){ var el = $(this), parent = el.parents('[mid]') }else{ parent = options.p || options.parent } if(!options.attribute){ options.attribute = '' } if(options.animate === true){ var zoomGlassAnimate = 'animate' }else{ var zoomGlassAnimate = 'css' } if(!options.magnifyOffsetElement){ options.magnifyOffsetElement = '.stream-block' } if(!options.targetForZoom){ options.targetForZoom = '.stream-element' } if(options.auto === true){ var streamBlockOperator = 'position' }else{ var streamBlockOperator = 'offset' } var magnifiedElement if(!options.videoUrl){ if(options.useCanvas === true){ magnifiedElement = 'canvas' }else{ magnifiedElement = 'iframe' } }else{ magnifiedElement = 'video' } if(!options.mon && !options.monitor){ var groupKey = parent.attr('ke')//group key var monitorId = parent.attr('mid')//monitor id var sessionKey = parent.attr('auth')//authkey var monitor = $.ccio.mon[groupKey + monitorId + sessionKey]//monitor configuration }else{ var monitor = options.mon || options.monitor var groupKey = monitor.ke//group key var monitorId = monitor.mid//monitor id var sessionKey = monitor.auth//authkey } if(options.zoomAmount)zoomAmount = 3 if(!zoomAmount)zoomAmount = 3 var realHeight = parent.attr('realHeight') var realWidth = parent.attr('realWidth') var height = parseFloat(realHeight) * zoomAmount//height of stream var width = parseFloat(realWidth) * zoomAmount//width of stream var targetForZoom = parent.find(options.targetForZoom) zoomGlass = parent.find(".zoomGlass") var zoomFrame = function(){ var magnify_offset = parent.find(options.magnifyOffsetElement)[streamBlockOperator]() var mx = options.pageX - magnify_offset.left var my = options.pageY - magnify_offset.top var rx = Math.round(mx/targetForZoom.width()*width - zoomGlass.width()/2)*-1 var ry = Math.round(my/targetForZoom.height()*height - zoomGlass.height()/2)*-1 var px = mx - zoomGlass.width()/2 var py = my - zoomGlass.height()/2 zoomGlass[zoomGlassAnimate]({left: px, top: py}).find(magnifiedElement)[zoomGlassAnimate]({left: rx, top: ry}) } var commit = function(height,width){ zoomGlass.find(magnifiedElement).css({ height: height, width: width }) zoomFrame() } if(!height || !width || zoomGlass.length === 0){ zoomGlass = parent.find(".zoomGlass") var zoomGlassShell = function(contents){return `
    ${contents}
    `} if(!options.videoUrl){ $.ccio.snapshot(monitor,function(url,buffer,w,h){ parent.attr('realWidth',w) parent.attr('realHeight',h) if(zoomGlass.length === 0){ if(options.useCanvas === true){ parent.append(zoomGlassShell('')) }else{ parent.append(zoomGlassShell('