From 634d718359cbdd08311556f832ab075766edecfe Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 23 Aug 2024 23:40:44 +0300 Subject: [PATCH 01/22] ".sticky #mfbpanel" height no more than 40% of the screen (skin.css) Relevant for mobile devices. --- web/skins/classic/css/base/skin.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index dff9f6f51..454a3b7fc 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -779,6 +779,11 @@ ul.nav.nav-pills.flex-column { float: left; } +.sticky #mfbpanel { + max-height: 40vh; /* For mobile devices */ + overflow: auto; +} + .chosen { position: absolute !important; left: -999em !important; } From 6feffbaefaf00d4b5ff21e8ed39c51178b425344 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 18 Sep 2024 13:34:18 +0300 Subject: [PATCH 02/22] Max height limit for .sticky #toolbar & .sticky #mfbpanel (skin.css) --- web/skins/classic/css/base/skin.css | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 454a3b7fc..e8a9fb8c7 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -776,12 +776,7 @@ ul.nav.nav-pills.flex-column { #mfbpanel { width: calc(100% - 25px); - float: left; -} - -.sticky #mfbpanel { - max-height: 40vh; /* For mobile devices */ - overflow: auto; + float: left; } .chosen { @@ -1123,6 +1118,19 @@ html::-webkit-scrollbar-thumb, div::-webkit-scrollbar-thumb, nav::-webkit-scroll } } +@media screen and (max-height:600px) { + .sticky #mfbpanel { + max-height: 40vh; /* For mobile devices */ + overflow-y: auto; + overflow-x: hidden; + } + + .sticky #toolbar { + max-height: 20vh; /* For mobile devices */ + overflow-y: auto; + overflow-x: hidden; + } +} /* +++ This block should always be located at the end! */ .hidden { display: none; From 0f37a71338401478ad7cd436fa160779a2b780c1 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 18 Sep 2024 13:35:49 +0300 Subject: [PATCH 03/22] Removed redundant comments (skin.css) --- web/skins/classic/css/base/skin.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index e8a9fb8c7..231f9db91 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -1120,13 +1120,13 @@ html::-webkit-scrollbar-thumb, div::-webkit-scrollbar-thumb, nav::-webkit-scroll @media screen and (max-height:600px) { .sticky #mfbpanel { - max-height: 40vh; /* For mobile devices */ + max-height: 40vh; overflow-y: auto; overflow-x: hidden; } .sticky #toolbar { - max-height: 20vh; /* For mobile devices */ + max-height: 20vh; overflow-y: auto; overflow-x: hidden; } From 334d84042518815f10416e664230fd4b64c16b97 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 18 Sep 2024 15:04:40 +0300 Subject: [PATCH 04/22] Restored accidentally deleted block (skin.css) --- web/skins/classic/css/base/skin.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index aa7a0e5f0..8574054e0 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -1201,6 +1201,12 @@ button.btn.btn-edit-monitor:hover, button.btn.btn-view-watch:hover { background-color: darkgrey; } + +.controlHeader .chosen-container { + /* limit width of monitor filters so that it fits on 1920px */ + max-width: 300px; +} + /* --- Control button block in the Stream image*/ /* +++ This block should always be located at the end! */ From d216c4cf4793c07aa5a2901b7ccc9b693a2a8ca2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Sep 2024 17:26:55 -0400 Subject: [PATCH 05/22] Remove STATE_TAPE --- web/includes/config.php.in | 1 - web/skins/classic/js/skin.js.php | 1 - 2 files changed, 2 deletions(-) diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 384d41181..9e91cb583 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -94,7 +94,6 @@ define('STATE_IDLE', 1); define('STATE_PREALARM', 2); define('STATE_ALARM', 3); define('STATE_ALERT', 4); -define('STATE_TAPE', 5); // // DVR Control Commands diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index db16d462a..5bc1b4891 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -107,7 +107,6 @@ const STATE_IDLE = ; const STATE_PREALARM = ; const STATE_ALARM = ; const STATE_ALERT = ; -const STATE_TAPE = ; const CMD_ANALYZE_ON = ; const CMD_ANALYZE_OFF = ; From ab1b67893aa9a719a982cb16b33716fe7df6a706 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Sep 2024 17:27:43 -0400 Subject: [PATCH 06/22] Remove unused getStream and watchdog stuff. When AYSW modal fires, pause cycle as well --- web/skins/classic/views/js/watch.js | 161 +--------------------------- 1 file changed, 5 insertions(+), 156 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 99a4fb361..c7c57bcff 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -148,135 +148,6 @@ function changeStreamQuality() { monitorsSetScale(monitorId); } -function getStreamCmdResponse(respObj, respText) { - watchdogOk('stream'); - streamCmdTimer = clearTimeout(streamCmdTimer); - if (respObj.result == 'Ok') { - // The get status command can get backed up, in which case we won't be able to get the semaphore and will exit. - if (respObj.status) { - const streamStatus = respObj.status; - if ($j('#viewingFPSValue').text() != streamStatus.fps) { - $j('#viewingFPSValue').text(streamStatus.fps); - } - if ($j('#captureFPSValue').text() != streamStatus.capturefps) { - $j('#captureFPSValue').text(streamStatus.capturefps); - } - if ($j('#analysisFPSValue').text() != streamStatus.analysisfps) { - $j('#analysisFPSValue').text(streamStatus.analysisfps); - } - - setAlarmState(streamStatus.state); - - $j('#levelValue').text(streamStatus.level); - var newClass = 'ok'; - if (streamStatus.level > 95) { - newClass = 'alarm'; - } else if (streamStatus.level > 80) { - newClass = 'alert'; - } - $j('#levelValue').removeClass(); - $j('#levelValue').addClass(newClass); - - var delayString = secsToTime(streamStatus.delay); - - if (streamStatus.paused == true) { - $j('#modeValue').text('Paused'); - $j('#rate').addClass('hidden'); - $j('#delayValue').text(delayString); - $j('#delay').removeClass('hidden'); - $j('#level').removeClass('hidden'); - streamCmdPause(false); - } else if (streamStatus.delayed == true) { - $j('#modeValue').text('Replay'); - $j('#rateValue').text(streamStatus.rate); - $j('#rate').removeClass('hidden'); - $j('#delayValue').text(delayString); - $j('#delay').removeClass('hidden'); - $j('#level').removeClass('hidden'); - if (streamStatus.rate == 1) { - streamCmdPlay(false); - } else if (streamStatus.rate > 0) { - if (streamStatus.rate < 1) { - streamCmdSlowFwd(false); - } else { - streamCmdFastFwd(false); - } - } else { - if (streamStatus.rate > -1) { - streamCmdSlowRev(false); - } else { - streamCmdFastRev(false); - } - } // rate - } else { - $j('#modeValue').text('Live'); - $j('#rate').addClass('hidden'); - $j('#delay').addClass('hidden'); - $j('#level').addClass('hidden'); - streamCmdPlay(false); - } // end if paused or delayed - - $j('#zoomValue').text(streamStatus.zoom); - if (streamStatus.zoom == '1.0') { - setButtonState('zoomOutBtn', 'unavail'); - } else { - setButtonState('zoomOutBtn', 'inactive'); - } - - if (canEdit.Monitors) { - if (streamStatus.enabled) { - enableAlmBtn.addClass('disabled'); - enableAlmBtn.prop('title', disableAlarmsStr); - if (streamStatus.forced) { - forceAlmBtn.addClass('disabled'); - forceAlmBtn.prop('title', cancelForcedAlarmStr); - } else { - forceAlmBtn.removeClass('disabled'); - forceAlmBtn.prop('title', forceAlarmStr); - } - forceAlmBtn.prop('disabled', false); - } else { - enableAlmBtn.removeClass('disabled'); - enableAlmBtn.prop('title', enableAlarmsStr); - forceAlmBtn.prop('disabled', true); - } - enableAlmBtn.prop('disabled', false); - } // end if canEdit.Monitors - - if (streamStatus.auth) { - auth_hash = streamStatus.auth; - // Try to reload the image stream. - var streamImg = $j('#liveStream'+monitorId); - if (streamImg) { - const oldSrc = streamImg.attr('src'); - if (oldSrc) { - const newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth); - if (oldSrc != newSrc) { - streamImg.attr('src', ''); // Required or chrome doesn't stop the stream - streamImg.attr('src', newSrc); - table.bootstrapTable('refresh'); - } - } - } - } // end if have a new auth hash - } // end if respObj.status - } else { - console.log("Not ok"); - checkStreamForErrors('getStreamCmdResponse', respObj);//log them - setTimeout(fetchImage, 1000, $j('#imageFeed img')[0]); - } - - var streamCmdTimeout = statusRefreshTimeout; - if (alarmState == STATE_ALARM || alarmState == STATE_ALERT) { - streamCmdTimeout = streamCmdTimeout/5; - } - streamCmdTimer = setTimeout(streamCmdQuery, streamCmdTimeout); -} - -function streamCmdQuery() { - streamCmdReq({command: CMD_QUERY}); -} - function onPause() { setButtonState('pauseBtn', 'active'); setButtonState('playBtn', 'inactive'); @@ -502,9 +373,9 @@ function cmdCancelForcedAlarm() { function cmdForce() { if (forceAlmBtn.hasClass('disabled')) { - cmdCancelForcedAlarm(); - } else { cmdForceAlarm(); + } else { + cmdCancelForcedAlarm(); } } @@ -753,31 +624,6 @@ function zoomOutClick(event) { } } -var watchdogInactive = { - 'stream': false, - 'status': false -}; - -var watchdogFunctions = { - 'stream': streamCmdQuery, - 'status': statusCmdQuery, -}; - -//Make sure the various refreshes are still taking effect -function watchdogCheck(type) { - if (watchdogInactive[type]) { - console.log("Detected streamWatch of type: " + type + " stopped, restarting"); - watchdogFunctions[type](); - watchdogInactive[type] = false; - } else { - watchdogInactive[type] = true; - } -} - -function watchdogOk(type) { - watchdogInactive[type] = false; -} - function reloadWebSite() { document.getElementById('imageFeed').innerHTML = document.getElementById('imageFeed').innerHTML; } @@ -1170,6 +1016,8 @@ function initPage() { setInterval(function() { if (idle >= ZM_WEB_VIEWING_TIMEOUT) { streamCmdPause(true); + const cycle_was = cycle; + cyclePause(); let ayswModal = $j('#AYSWModal'); if (!ayswModal.length) { $j.getJSON('?request=modal&modal=areyoustillwatching') @@ -1177,6 +1025,7 @@ function initPage() { ayswModal = insertModalHtml('AYSWModal', data.html); $j('#AYSWYesBtn').on('click', function() { streamCmdPlay(true); + if (cycle_was) cycleStart(); idle = 0; }); ayswModal.modal('show'); From 689881d105e7aca5acd3a8914bf29222baf6c2a4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Sep 2024 17:29:21 -0400 Subject: [PATCH 07/22] Use monitorStream.play and pause instead of assuming zms --- web/skins/classic/views/js/watch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index c7c57bcff..5416b28c2 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -163,7 +163,7 @@ function onPause() { function streamCmdPause(action) { onPause(); if (action) { - monitorStream.streamCommand(CMD_PAUSE); + monitorStream.pause(); } } @@ -198,7 +198,7 @@ function streamCmdPlay(action) { if (action) { if (monitorStream.started) { //Stream was on pause - monitorStream.streamCommand(CMD_PLAY); + monitorStream.play(); } else { //Stream has been stopped monitorStream.start(); From c9aa03dc5572c258fe13e1a20302f8b716dfc615 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 10:35:43 -0400 Subject: [PATCH 08/22] You can't change size in a ResizeObserver. So do what we do in montage, just add the affected element to an array and deal with it in an interval --- web/skins/classic/views/js/watch.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 5416b28c2..d1c435bdf 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -10,6 +10,7 @@ var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms] var idle = 0; var monitorStream = false; /* Stream is not started */ var currentMonitor; +var changedMonitors = []; var classSidebarL = 'col-sm-3'; /* id="sidebar" */ var classSidebarR = 'col-sm-2'; /* id="ptzControls" */ @@ -1063,8 +1064,12 @@ function initPage() { // Creating a ResizeObserver Instance observer = new ResizeObserver((objResizes) => { + // We can't do the actual resizing in the Observer, it causes a loop. Need to just set a flag and do it in an interval. objResizes.forEach((obj) => { - monitorsSetScale(monitorId); + const id = stringToNumber(obj.target.id); + if (!changedMonitors.includes(id)) { + changedMonitors.push(id); + } }); }); @@ -1073,6 +1078,16 @@ function initPage() { observer.observe(this); }); + setInterval(() => { //Updating GridStack resizeToContent, Scale & Ratio + if (changedMonitors.length > 0) { + changedMonitors.slice().reverse().forEach(function(item, index, object) { + const img = document.getElementById('liveStream'+item); + changedMonitors.splice(object.length - 1 - index, 1); + monitorsSetScale(item); + }); + } + }, 100); + // Event listener for double click //var elStream = document.querySelectorAll('[id ^= "liveStream"], [id ^= "evtStream"]'); var elStream = document.querySelectorAll('[id = "wrapperMonitor"]'); From d6f28f5c624da70c81b8be0c7bf7b9ca54988f05 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 10:44:28 -0400 Subject: [PATCH 09/22] Update eslintignore to ignore bootstrap-table 1.23.3. Remove mootools reference --- .eslintignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.eslintignore b/.eslintignore index c70df40ba..c495c2faa 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,7 +7,7 @@ web/js/videojs.zoomrotate.js web/js/fontfaceobserver.standalone.js web/skins/classic/js/bootstrap-4.5.0.js web/skins/classic/js/bootstrap.bundle.min.js -web/skins/classic/js/bootstrap-table-1.22.3 +web/skins/classic/js/bootstrap-table-1.23.3 web/skins/classic/js/chosen web/skins/classic/js/dateTimePicker web/skins/classic/js/jquery-*.js @@ -16,7 +16,6 @@ web/skins/classic/js/jquery.js web/skins/classic/js/moment.js web/skins/classic/js/video.js web/skins/classic/assets -web/tools/mootools web/js/janus.js web/js/ajaxQueue.js web/js/hls.js From a6db032301b9d5356f3066ecd9774dd4d976b530 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 10:44:42 -0400 Subject: [PATCH 10/22] Remove tools dir from .gitignore --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index a856b1d5f..ee6ac35e7 100644 --- a/.gitignore +++ b/.gitignore @@ -199,10 +199,6 @@ web/cmake_install.cmake web/events/ web/images/ web/includes/config.php -web/tools/mootools/CMakeFiles/ -web/tools/mootools/cmake_install.cmake -web/tools/mootools/mootools-core.js -web/tools/mootools/mootools-more.js web/undef.log zm.conf zmconfgen.pl From 564e521c8c3e8c8678bdb10481054d27e146cb2d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 10:48:27 -0400 Subject: [PATCH 11/22] Fix eslint --- web/skins/classic/views/js/watch.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index d1c435bdf..a08742dd5 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1001,7 +1001,7 @@ function initPage() { bindButton('#cyclePrevBtn', 'click', null, cyclePrev); bindButton('#cycleToggle', 'click', null, cycleToggle); bindButton('#cyclePeriod', 'change', null, cyclePeriodChange); - if (monitorData.length && cycle) { + if (monitorData.length > 1 && cycle) { cycleStart(); } else { cyclePause(); @@ -1081,7 +1081,6 @@ function initPage() { setInterval(() => { //Updating GridStack resizeToContent, Scale & Ratio if (changedMonitors.length > 0) { changedMonitors.slice().reverse().forEach(function(item, index, object) { - const img = document.getElementById('liveStream'+item); changedMonitors.splice(object.length - 1 - index, 1); monitorsSetScale(item); }); From e7d4fa334ddd0b9377694b713a1157dc33a396e0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 10:53:47 -0400 Subject: [PATCH 12/22] Initialize lastPTS and only set lastAudioPTS if the stream is audio. --- src/zm_ffmpeg_camera.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index f5ee58c1e..68a13d1fe 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -188,7 +188,7 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { start_read_time = std::chrono::steady_clock::now(); int ret; AVFormatContext *formatContextPtr; - int64_t lastPTS; + int64_t lastPTS = -1; if ( mSecondFormatContext and ( @@ -242,7 +242,7 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { } return -1; } - if ( packet->stream_index == mAudioStreamId) { + if (packet->stream_index == mAudioStreamId) { lastPTS = mLastAudioPTS; } else if ( packet->stream_index == mVideoStreamId) { lastPTS = mLastVideoPTS; @@ -287,7 +287,7 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { mFirstVideoPTS = packet->pts; mLastVideoPTS = packet->pts - mFirstVideoPTS; - } else { + } else if (stream == mAudioStream) { if (mFirstAudioPTS == AV_NOPTS_VALUE) mFirstAudioPTS = packet->pts; From 53ffcd4121f66f513c80da50b6c0b509038ddb1b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 14:54:58 -0400 Subject: [PATCH 13/22] In play/pause, start/stop rtsp2web as well as zms stream. Also stop status requests. Rework the enable/disable alarms and force alarm buttons when using rtsp2web. --- web/js/MonitorStream.js | 75 ++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index a409f51a1..cf1295f98 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -50,7 +50,7 @@ function MonitorStream(monitorData) { }; this.img_onerror = function() { - console.log('Image stream has been stoppd! stopping streamCmd'); + console.log('Image stream has been stopped! stopping streamCmd'); this.streamCmdTimer = clearInterval(this.streamCmdTimer); }; this.img_onload = function() { @@ -375,10 +375,21 @@ function MonitorStream(monitorData) { }; this.pause = function() { - this.streamCommand(CMD_PAUSE); + if (this.element.src) { + this.streamCommand(CMD_PAUSE); + } else { + this.element.pause(); + this.statusCmdTimer = clearInterval(this.statusCmdTimer); + } }; + this.play = function() { - this.streamCommand(CMD_PLAY); + if (this.element.src) { + this.streamCommand(CMD_PLAY); + } else { + this.element.play(); + this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout); + } }; this.eventHandler = function(event) { @@ -681,7 +692,6 @@ function MonitorStream(monitorData) { this.getStatusCmdResponse=function(respObj, respText) { //watchdogOk('status'); if (respObj.result == 'Ok') { - const monitorStatus = respObj.monitor.Status; const captureFPSValue = $j('#captureFPSValue'+this.id); const analysisFPSValue = $j('#analysisFPSValue'+this.id); const viewingFPSValue = $j('#viewingFPSValue'+this.id); @@ -720,42 +730,45 @@ function MonitorStream(monitorData) { } if (canEdit.Monitors) { - if (monitorStatus.enabled) { - if ('enableAlarmButton' in this.buttons) { + if ('enableAlarmButton' in this.buttons) { + if (monitor.Analysing == 'None') { + // Not doing analysis, so enable/disable button should be grey + if (!this.buttons.enableAlarmButton.hasClass('disabled')) { this.buttons.enableAlarmButton.addClass('disabled'); this.buttons.enableAlarmButton.prop('title', disableAlarmsStr); } - } - if ('forceAlarmButton' in this.buttons) { - if (monitorStatus.forced) { - if (!this.buttons.forceAlarmButton.hasClass('disabled')) { - this.buttons.forceAlarmButton.addClass('disabled'); - this.buttons.forceAlarmButton.prop('title', cancelForcedAlarmStr); - } - } else { - if (this.buttons.forceAlarmButton.hasClass('disabled')) { - this.buttons.forceAlarmButton.removeClass('disabled'); - this.buttons.forceAlarmButton.prop('title', forceAlarmStr); - } - } - this.buttons.forceAlarmButton.prop('disabled', false); - } - } else { - if ('enableAlarmButton' in this.buttons) { + } else { this.buttons.enableAlarmButton.removeClass('disabled'); this.buttons.enableAlarmButton.prop('title', enableAlarmsStr); - } - if ('forceAlarmButton' in this.buttons) { - this.buttons.forceAlarmButton.prop('disabled', true); - } - } - if ('enableAlarmButton' in this.buttons) { + } // end if doing analysis this.buttons.enableAlarmButton.prop('disabled', false); + } // end if have enableAlarmButton + + if ('forceAlarmButton' in this.buttons) { + if (monitor.Status == STATE_ALARM || monitor.Status == STATE_ALERT) { + // Ic0n: My thought here is that the non-disabled state should be for killing an alarm + // and the disabled state should be to force an alarm + if (this.buttons.forceAlarmButton.hasClass('disabled')) { + this.buttons.forceAlarmButton.removeClass('disabled'); + this.buttons.forceAlarmButton.prop('title', cancelForcedAlarmStr); + } + } else { + if (!this.buttons.forceAlarmButton.hasClass('disabled')) { + // Looks disabled + this.buttons.forceAlarmButton.addClass('disabled'); + this.buttons.forceAlarmButton.prop('title', forceAlarmStr); + } + } + this.buttons.forceAlarmButton.prop('disabled', false); + } else { + console.log("No forceAlarmButton"); } + } else { + console.log("Can't edit"); } // end if canEdit.Monitors - this.setAlarmState(monitorStatus); + this.setAlarmState(monitor.Status); if (respObj.auth_hash) { if (auth_hash != respObj.auth_hash) { @@ -807,7 +820,7 @@ function MonitorStream(monitorData) { this.alarmCommand = function(command) { if (this.ajaxQueue) { - console.log('Aborting in progress ajax for alarm'); + console.log('Aborting in progress ajax for alarm', this.ajaxQueue); // Doing this for responsiveness, but we could be aborting something important. Need smarter logic this.ajaxQueue.abort(); } From 3a58ca5ad3aaf8962df86150dfe4d25632f4514e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Sep 2024 15:09:42 -0400 Subject: [PATCH 14/22] code improvements and tidy ups In ResizingObserver, check for existence of div. The outgoing monitor may fire a change after it's html is gone. When becoming visible again only restart stream if Are You Still Watching is not being displayed --- web/skins/classic/views/js/watch.js | 77 +++++++++++++---------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index a08742dd5..b6a395c96 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -796,7 +796,7 @@ function streamPrepareStart(monitor=null) { // Update table links each time after new data is loaded table.on('post-body.bs.table', function(data) { - var thumb_ndx = $j('#eventList tr th').filter(function() { + const thumb_ndx = $j('#eventList tr th').filter(function() { return $j(this).text().trim() == 'Thumbnail'; }).index(); table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail'); @@ -840,11 +840,7 @@ function handleMouseLeave(event) { } function streamStart(monitor = null) { - if (monitor) { - monitorStream = new MonitorStream(monitor); - } else { - monitorStream = new MonitorStream(monitorData[monIdx]); - } + monitorStream = new MonitorStream(monitor ? monitor : monitorData[monIdx]); monitorStream.setBottomElement(document.getElementById('dvrControls')); // Start the fps and status updates. give a random delay so that we don't assault the server //monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val()); @@ -874,15 +870,17 @@ function streamStart(monitor = null) { } function streamReStart(oldId, newId) { - document.getElementById('monitor').classList.add('hidden-shift'); - const el = document.querySelector('.imageFeed'); - const newMonitorName = document.getElementById('nav-item-cycle'+newId).querySelector('a').textContent; + const monitor_div = document.getElementById('monitor'); + monitor_div.classList.add('hidden-shift'); + currentMonitor = monitorData.find((o) => { return parseInt(o["id"]) === newId; }); const url = new URL(document.location.href); monitorId = newId; filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId; + + const newMonitorName = document.getElementById('nav-item-cycle'+newId).querySelector('a').textContent; document.querySelector('title').textContent = newMonitorName; url.searchParams.set('mid', monitorId); history.pushState(null, "", url); @@ -891,11 +889,13 @@ function streamReStart(oldId, newId) { if (monitorStream) { monitorStream.kill(); } + + const el = document.querySelector('.imageFeed'); el.removeEventListener('mouseenter', handleMouseEnter); el.removeEventListener('mouseleave', handleMouseLeave); //Change main monitor block - document.getElementById('monitor').innerHTML = currentMonitor.streamHTML; + monitor_div.innerHTML = currentMonitor.streamHTML; //Change active element of the navigation menu document.getElementById('nav-item-cycle'+oldId).querySelector('a').classList.remove("active"); @@ -1042,6 +1042,7 @@ function initPage() { setInterval(() => { //Updating Scale. When quickly scrolling the mouse wheel or quickly pressing Zoom In/Out, you should not set Scale very often. if (updateScale) { + console.log('set scale for ', monitorId); monitorsSetScale(monitorId); updateScale = false; } @@ -1060,7 +1061,7 @@ function initPage() { if (currentMonitor) { applyMonitorControllable(); } - streamPrepareStart(); + streamPrepareStart(currentMonitor); // Creating a ResizeObserver Instance observer = new ResizeObserver((objResizes) => { @@ -1082,7 +1083,9 @@ function initPage() { if (changedMonitors.length > 0) { changedMonitors.slice().reverse().forEach(function(item, index, object) { changedMonitors.splice(object.length - 1 - index, 1); - monitorsSetScale(item); + // When changing monitor, this may fire after we have replace the monitor html + if (document.getElementById('monitor'+item)) + monitorsSetScale(item); }); } }, 100); @@ -1147,6 +1150,7 @@ function cycleStop(target) { cyclePause(); } +// FIXME this runs within the interval handler and can take >50ms which will cause chrome to complain. function cycleNext() { monIdx ++; if (monIdx >= monitorData.length) { @@ -1156,18 +1160,12 @@ function cycleNext() { console.log('No monitorData for ' + monIdx); } clearInterval(intervalId); - monitorStream.kill(); // +++ Start next monitor - let oldId; - if (monIdx == 0) { - oldId = monitorData[monitorData.length-1].id; - } else { - oldId = monitorData[monIdx-1].id; - } + const oldId = monitorData[(monIdx == 0) ? monitorData.length-1 : monIdx-1].id; const newId = monitorData[monIdx].id; streamReStart(oldId, newId); - cycleStart(); + if (cycle) cycleStart(); // --- Start next monitor //window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); } @@ -1181,19 +1179,12 @@ function cyclePrev() { console.log('No monitorData for ' + monIdx); } clearInterval(intervalId); - //monitorStream.stop(); - monitorStream.kill(); // +++ Start previous monitor - let oldId; - if (monIdx == monitorData.length - 1) { - oldId = monitorData[0].id; - } else { - oldId = monitorData[monIdx+1].id; - } + const oldId = monitorData[(monIdx == monitorData.length - 1)? 0 : monIdx+1].id; const newId = monitorData[monIdx].id; streamReStart(oldId, newId); - cycleStart(); + if (cycle) cycleStart(); // --- Start previous monitors //window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); } @@ -1375,21 +1366,23 @@ function monitorsSetScale(id=null) { overrideHW = true; } + const monitor_div = document.getElementById('monitor'+id); + if (!monitor_div) console.log("No monitor div for ", id); if (resize) { if (scale == '0') { - document.getElementById('monitor'+id).style.width = 'max-content'; //Required when switching from resize=false to resize=true + monitor_div.style.width = 'max-content'; //Required when switching from resize=false to resize=true } - document.getElementById('monitor'+id).style.maxWidth = maxWidth; + monitor_div.style.maxWidth = maxWidth; if (!landscape) { //PORTRAIT - document.getElementById('monitor'+id).style.width = 'max-content'; + monitor_div.style.width = 'max-content'; document.getElementById('liveStream'+id).style.height = height; } } else { document.getElementById('liveStream'+id).style.height = ''; - document.getElementById('monitor'+id).style.width = width; - document.getElementById('monitor'+id).style.maxWidth = ''; + monitor_div.style.width = width; + monitor_div.style.maxWidth = ''; if (scale == 'fit_to_width') { - document.getElementById('monitor'+id).style.width = ''; + monitor_div.style.width = ''; } else if (scale == '100') { document.getElementById('liveStream'+id).style.width = width; } @@ -1398,10 +1391,10 @@ function monitorsSetScale(id=null) { curentMonitor.setScale(defScale, width, height, {resizeImg: resize, scaleImg: panZoomScale, streamQuality: $j('#streamQuality').val()}); if (overrideHW) { if (!landscape) { //PORTRAIT - document.getElementById('monitor'+id).style.width = 'max-content'; + monitor_div.style.width = 'max-content'; } else { document.getElementById('liveStream'+id).style.height = 'auto'; - document.getElementById('monitor'+id).style.width = 'auto'; + monitor_div.style.width = 'auto'; } } } else { @@ -1437,20 +1430,20 @@ function monitorsSetScale(id=null) { } if (resize) { - document.getElementById('monitor'+id).style.width = 'max-content'; //Required when switching from resize=false to resize=true + monitor_div.style.width = 'max-content'; //Required when switching from resize=false to resize=true } //monitors[i].setScale(0, parseInt(el.clientWidth * panZoomScale) + 'px', parseInt(el.clientHeight * panZoomScale) + 'px', {resizeImg:true, scaleImg:panZoomScale}); monitors[i].setScale(0, width, height, {resizeImg: resize, scaleImg: panZoomScale}); if (!resize) { document.getElementById('liveStream'+id).style.height = ''; if (scale == 'fit_to_width') { - document.getElementById('monitor'+id).style.width = ''; + monitor_div.style.width = ''; } else if (scale == '100') { - document.getElementById('monitor'+id).style.width = 'max-content'; + monitor_div.style.width = 'max-content'; document.getElementById('liveStream'+id).style.width = width; } } - } + } // end foreach monitor } setButtonSizeOnStream(); } @@ -1469,7 +1462,7 @@ document.onvisibilitychange = () => { }, 15*1000); } else { //Start monitor when show page - if (monitorStream && !monitorStream.started) { + if (monitorStream && !monitorStream.started && (idle Date: Wed, 25 Sep 2024 15:17:59 -0400 Subject: [PATCH 15/22] Fix eslint --- web/skins/classic/views/js/watch.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index b6a395c96..f2031f5bb 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1084,8 +1084,9 @@ function initPage() { changedMonitors.slice().reverse().forEach(function(item, index, object) { changedMonitors.splice(object.length - 1 - index, 1); // When changing monitor, this may fire after we have replace the monitor html - if (document.getElementById('monitor'+item)) + if (document.getElementById('monitor'+item)) { monitorsSetScale(item); + } }); } }, 100); From f4830b1c14077acfdb0eba8b3b762b032fdc7f4f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Sep 2024 07:10:47 -0400 Subject: [PATCH 16/22] Revert "You can't change size in a ResizeObserver. So do what we do in montage, just add the affected element to an array and deal with it in an interval" This reverts commit c9aa03dc5572c258fe13e1a20302f8b716dfc615. --- web/skins/classic/views/js/watch.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index f2031f5bb..3961b4eb2 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -10,7 +10,6 @@ var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms] var idle = 0; var monitorStream = false; /* Stream is not started */ var currentMonitor; -var changedMonitors = []; var classSidebarL = 'col-sm-3'; /* id="sidebar" */ var classSidebarR = 'col-sm-2'; /* id="ptzControls" */ @@ -1065,12 +1064,8 @@ function initPage() { // Creating a ResizeObserver Instance observer = new ResizeObserver((objResizes) => { - // We can't do the actual resizing in the Observer, it causes a loop. Need to just set a flag and do it in an interval. objResizes.forEach((obj) => { - const id = stringToNumber(obj.target.id); - if (!changedMonitors.includes(id)) { - changedMonitors.push(id); - } + monitorsSetScale(monitorId); }); }); @@ -1079,18 +1074,6 @@ function initPage() { observer.observe(this); }); - setInterval(() => { //Updating GridStack resizeToContent, Scale & Ratio - if (changedMonitors.length > 0) { - changedMonitors.slice().reverse().forEach(function(item, index, object) { - changedMonitors.splice(object.length - 1 - index, 1); - // When changing monitor, this may fire after we have replace the monitor html - if (document.getElementById('monitor'+item)) { - monitorsSetScale(item); - } - }); - } - }, 100); - // Event listener for double click //var elStream = document.querySelectorAll('[id ^= "liveStream"], [id ^= "evtStream"]'); var elStream = document.querySelectorAll('[id = "wrapperMonitor"]'); From d8cfce4c4ad985ead7a26c5d5f3bc9cee75a8f5c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Sep 2024 07:14:19 -0400 Subject: [PATCH 17/22] INstead of the code in c9aa03d, just set updateScale. --- web/skins/classic/views/js/watch.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 3961b4eb2..c4bf0a81c 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1064,9 +1064,7 @@ function initPage() { // Creating a ResizeObserver Instance observer = new ResizeObserver((objResizes) => { - objResizes.forEach((obj) => { - monitorsSetScale(monitorId); - }); + updateScale = true; }); // Registering an observer on an element From 0ee28cad060ce386d24437f689d5479f01d9da71 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Sep 2024 07:50:01 -0400 Subject: [PATCH 18/22] FIx command => streamCommand. Fixes #4150 --- web/skins/classic/views/js/watch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index c4bf0a81c..7ae98ed3e 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -254,7 +254,7 @@ function streamCmdSlowFwd(action) { setButtonState('fastRevBtn', 'inactive'); } if (action) { - monitorStream.command(CMD_SLOWFWD); + monitorStream.streamCommand(CMD_SLOWFWD); } setButtonState('pauseBtn', 'active'); if (monitorStreamReplayBuffer) { @@ -273,7 +273,7 @@ function streamCmdSlowRev(action) { setButtonState('fastRevBtn', 'inactive'); } if (action) { - monitorStream.command(CMD_SLOWREV); + monitorStream.streamCommand(CMD_SLOWREV); } setButtonState('pauseBtn', 'active'); if (monitorStreamReplayBuffer) { @@ -292,7 +292,7 @@ function streamCmdFastRev(action) { setButtonState('fastRevBtn', 'inactive'); } if (action) { - monitorStream.command(CMD_FASTREV); + monitorStream.streamCommand(CMD_FASTREV); } } From d4277bdb457ffc27686fa311260639a95f3c58f6 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Thu, 26 Sep 2024 16:34:52 +0300 Subject: [PATCH 19/22] Fix: Declared variable "spanTime" (event.js) Closed https://forums.zoneminder.com/viewtopic.php?t=33468&sid=1a292ca9e8daa501e2c411ce76d7e8c4 --- web/skins/classic/views/js/event.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index a9fdc252f..4920191df 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -199,6 +199,7 @@ function renderAlarmCues(containerEl) { // if we shouldn't just use the event length endtime-starttime var cueRatio = containerEl.width() / (event_length * 100); var minAlarm = Math.ceil(1/cueRatio); + var spanTime = 0; var spanTimeStart = 0; var spanTimeEnd = 0; var alarmed = 0; From 40f319cc8ed2cd71c815b9cb2dc74b0dda0edabf Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Sep 2024 16:13:34 -0400 Subject: [PATCH 20/22] Fix map not loading --- web/skins/classic/views/js/event.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 4920191df..4f91ea4e5 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -1668,7 +1668,7 @@ function initPage() { mapDiv.style.height='450px'; } if ( window.L ) { - map = L.map('LocationMap', { + const map = L.map('LocationMap', { center: L.latLng(eventData.Latitude, eventData.Longitude), zoom: 8, onclick: function() { From 202fc31abb33d62dc528a0dfd91023557e83e485 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 27 Sep 2024 11:13:41 +0300 Subject: [PATCH 21/22] Fix: Added declaration of variables "futNone, indexPlus" (event.js) Closed: https://forums.zoneminder.com/viewtopic.php?p=135636&sid=0e728ac75cf2a1ce0b42380582f5b928#p135636 --- web/skins/classic/views/js/event.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 4920191df..a69c7ada7 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -231,8 +231,8 @@ function renderAlarmCues(containerEl) { //console.log(left, frame.Delta, event_length, containerEl.width()); spanTimeStart = spanTimeEnd; } else if ( (frame.Type !== 'Alarm') && (alarmed == 1) ) { //from alarm to nothing. End alarm and start nothing. - futNone = 0; - indexPlus = i+1; + let futNone = 0; + let indexPlus = i+1; if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { //alarm is too short and there is more event continue; From 3802ec17d3722583a133e815b1cf99fa18c3958a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Sep 2024 10:01:09 -0400 Subject: [PATCH 22/22] Add test for 0 duration may fix floating point exceptions --- src/zm_eventstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 10ce522f4..ffdb8f63e 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -289,7 +289,7 @@ bool EventStream::loadEventData(uint64_t event_id) { frame.in_db); } // end foreach db row - if (event_data->end_time.time_since_epoch() != Seconds(0)) { + if (event_data->end_time.time_since_epoch() != Seconds(0) and event_data->duration != Seconds(0)) { Microseconds delta; if (!last_frame) { // There were no frames in db