From 32add0952c35bd86f43ba84b7c61b9689ecfbe51 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 10 Jan 2023 14:19:40 -0500 Subject: [PATCH 01/43] Differentiate between negative pts and AV_NOPTS_VALUE when looking for wrap around. --- src/zm_ffmpeg_camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 6bd2d0aac..629b4a675 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -227,7 +227,7 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { } return -1; } - if ((packet->pts < 0) and (lastPTS >=0)) { + if ((packet->pts < 0) and (packet->pts != AV_NOPTS_VALUE) and (lastPTS >= 0)) { // 32-bit wrap around? Info("Suspected 32bit wraparound in input pts. %" PRId64, packet->pts); return -1; From 7bac75dc35b9a7313391207bca1f15739c92bba4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Jan 2023 15:41:39 -0500 Subject: [PATCH 02/43] Add zoneminder org to githuib sponsors --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5994f0e6b..22e432561 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: [zoneminder,connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: zoneminder # Replace with a single Patreon username open_collective: zoneminder # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username From 4dbc198fd054a6d9cc6f5a331b865d1e2f9691af Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Jan 2023 09:03:48 -0500 Subject: [PATCH 03/43] Only load events when not in live mode --- web/skins/classic/views/js/montagereview.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 3caa8d326..dd5a88dec 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -1093,11 +1093,11 @@ function initPage() { }); }); - for (const event_id in events) { - load_Frames(events[event_id]); - } if ( !liveMode ) { + for (const event_id in events) { + load_Frames(events[event_id]); + } canvas = document.getElementById('timeline'); canvas.addEventListener('mousemove', mmove, false); From 95f9e0b19ad1c6ccd175d8c9d60bf75305563c5b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Jan 2023 09:25:39 -0500 Subject: [PATCH 04/43] Fix crash from getting packet from rend --- src/zm_videostore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 5171b5fad..72b58f976 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -983,10 +983,10 @@ int VideoStore::writePacket(const std::shared_ptr &zm_pkt) { } // end while if (have_out_of_order) { - AVPacket *p = ((*rit)->packet).get(); if (rit == queue.rend()) { - Warning("Unable to re-order packet, packet dts is %" PRId64, p->dts); + Debug(1, "Unable to re-order packet, packet dts is %" PRId64, av_pkt->dts); } else { + AVPacket *p = ((*rit)->packet).get(); Debug(1, "Found out of order packet, inserting after %" PRId64, p->dts); } queue.insert(rit.base(), zm_pkt); From 427201abd568acd68a091ddecb1285bfcd52ada0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Jan 2023 09:37:53 -0500 Subject: [PATCH 05/43] Fix eslint --- web/skins/classic/views/js/montagereview.js | 72 ++++++++++----------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index dd5a88dec..777a2aae5 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -86,7 +86,7 @@ function findFrameByTime(arr, time) { //console.log(keys); //console.log(keys[start]); // Iterate while start not meets end - //console.log("Looking for "+ time+ "start: " + start + ' end ' + end, arr[keys[start]]); + //console.log("Looking for "+ time+ "start: " + start + ' end ' + end, arr[keys[start]]); while ((start <= end)) { //&& arr[keys[start]] && (arr[keys[start]].TimeStampSecs <= time) && (arr[keys[end]].NextTimeStampSecs >= time)) { // Find the mid index @@ -101,7 +101,7 @@ function findFrameByTime(arr, time) { (!frame.NextTimeStampSecs) || // only if event.EndTime is null (frame.NextTimeStampSecs > time) ) - ) { + ) { //console.log("Found it at ", frame); return frame; @@ -1184,10 +1184,10 @@ function initPage() { el = $j(this); //el.on('change', changeDateTime()); if (el.hasClass('datetimepicker')) { - el.datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}) + el.datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); } if (el.hasClass('datepicker')) { - el.datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}) + el.datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); } }); } @@ -1204,7 +1204,7 @@ function takeSnapshot() { server = new Server(Servers[serverId]); $j.ajax({ method: 'POST', - url: server.UrlToApi()+'/snapshots.json' + (auth_relay ? '?' + auth_relay : ''), + url: server.urlToApi()+'/snapshots.json' + (auth_relay ? '?' + auth_relay : ''), data: { 'monitor_ids[]': monitorIndex.keys()}, success: function(response) { console.log(response); @@ -1221,10 +1221,10 @@ window.addEventListener('DOMContentLoaded', initPage); function load_Frames(zm_event) { return new Promise(function(resolve, reject) { - $j.getJSON(Servers[serverId].UrlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay) - .done(function(data) { - if (data.frames.length) { - /* + $j.getJSON(Servers[serverId].urlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay) + .done(function(data) { + if (data.frames.length) { + /* const zm_event = events[data.frames[0].Frame.EventId]; if (!zm_event) { console.error("No event object found for " + data.frames[0].Frame.EventId); @@ -1232,33 +1232,33 @@ function load_Frames(zm_event) { return; } */ - zm_event.FramesById = []; - let last_frame = null; + zm_event.FramesById = []; + let last_frame = null; - for (let i=0, len=data.frames.length; i Date: Thu, 12 Jan 2023 09:38:13 -0500 Subject: [PATCH 06/43] eslint doesn't like function names that start with a capital --- web/js/Server.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/web/js/Server.js b/web/js/Server.js index da439dd39..821862ed1 100644 --- a/web/js/Server.js +++ b/web/js/Server.js @@ -34,17 +34,18 @@ var Server = function() { } }, { - key: 'UrlToZMS', - value: function UrlToZMS() { + key: 'urlToZMS', + value: function urlToZMS() { const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToZMS && this.PathToZMS != 'null' ? this.PathToZMS : ''); } }, { - key: 'UrlToApi', - value: function UrlToApi() { + key: 'urlToApi', + value: function urlToApi() { const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); + //return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); + return this.Protocol + '://' + 'zm.connortechnology.com' + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); } } ]); From a8c80fde27ecb4eee03e067630a019a58d32dad0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Jan 2023 17:22:13 -0500 Subject: [PATCH 07/43] Update monitor preset view: Use a submit button instead of input with javascript. Remove no longer needed js code. Sort presets by Name. --- web/skins/classic/views/js/monitorpreset.js | 9 +----- web/skins/classic/views/monitorpreset.php | 34 ++++++++++----------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/web/skins/classic/views/js/monitorpreset.js b/web/skins/classic/views/js/monitorpreset.js index 1b8fa5e58..9c29ed6ac 100644 --- a/web/skins/classic/views/js/monitorpreset.js +++ b/web/skins/classic/views/js/monitorpreset.js @@ -1,12 +1,5 @@ -var form = $j('#monitorPresetForm'); - -function submitPreset( element ) { - form.target = opener.name; - form.view.value = 'monitor'; - form.submit(); -} - function configureButtons() { + const form = document.getElementById('monitorPresetForm'); form.saveBtn.disabled = (form.preset.selectedIndex==0); } diff --git a/web/skins/classic/views/monitorpreset.php b/web/skins/classic/views/monitorpreset.php index 645872943..ac4491432 100644 --- a/web/skins/classic/views/monitorpreset.php +++ b/web/skins/classic/views/monitorpreset.php @@ -18,18 +18,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canEdit( 'Monitors' ) ) -{ - $view = "error"; - return; -} -$sql = "select Id,Name from MonitorPresets"; -$presets = array(); -$presets[0] = translate('ChoosePreset'); -foreach( dbFetchAll( $sql ) as $preset ) -{ - $presets[$preset['Id']] = htmlentities( $preset['Name'] ); +if (!canEdit('Monitors')) { + $view = 'error'; + return; } +$mid = isset($_REQUEST['mid']) ? validInt($_REQUEST['mid']) : 0; $focusWindow = true; @@ -38,20 +31,27 @@ xhtmlHeaders(__FILE__, translate('MonitorPreset') );
-

+

- - + +

- + +

- - + +
From 8b75a2510a369652a6ddfd7ab3c988e670581c36 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Jan 2023 13:07:29 -0500 Subject: [PATCH 08/43] Change to ssize_t to fix compile on freebsd --- src/zm_sendfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_sendfile.h b/src/zm_sendfile.h index 4f5452c08..d30dc1460 100644 --- a/src/zm_sendfile.h +++ b/src/zm_sendfile.h @@ -13,7 +13,7 @@ /* Function to send the contents of a file. Will use sendfile or fall back to reading/writing */ -ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { +ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, ssize_t size) { #ifdef HAVE_SENDFILE4_SUPPORT ssize_t err = sendfile(out_fd, in_fd, offset, size); if (err < 0) { From b1486783e31259d8946a7f5b9f52ae485f6d3b9d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Jan 2023 12:34:51 -0500 Subject: [PATCH 09/43] Add a 5 second cycle option --- web/skins/classic/views/watch.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index a875980fb..17c6fdee4 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -239,6 +239,7 @@ $seconds = translate('seconds'); $minute = translate('minute'); $minutes = translate('minutes'); $cyclePeriodOptions = array( + 5 => '5 '.$seconds, 10 => '10 '.$seconds, 30 => '30 '.$seconds, 60 => '1 '.$minute, From 1236b366a15c4cd8eaf3e2d1ac24d893b4a61eff Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Jan 2023 12:48:02 -0500 Subject: [PATCH 10/43] Need to index into servers array using 0 not '' --- web/skins/classic/js/skin.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 28935aa4a..d567865cf 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -39,7 +39,7 @@ const cancelString = ''; try to avoid using PHP_SELF but here I try to replace everything after '.php'. */ ?> const thisUrl = ''; const skinPath = ''; -const serverId = ''; +const serverId = ; const Servers = []; Date: Fri, 13 Jan 2023 12:48:26 -0500 Subject: [PATCH 11/43] Remove reference to zm.connortechnology.com --- web/js/Server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/js/Server.js b/web/js/Server.js index 821862ed1..b861bbd9c 100644 --- a/web/js/Server.js +++ b/web/js/Server.js @@ -44,8 +44,7 @@ var Server = function() { key: 'urlToApi', value: function urlToApi() { const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - //return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); - return this.Protocol + '://' + 'zm.connortechnology.com' + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); + return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); } } ]); From cb87d04b3ec00e0e0d98508e6373a22ea4668215 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Jan 2023 13:36:48 -0500 Subject: [PATCH 12/43] Move api page content from options.php to _options_api.php --- web/skins/classic/views/_options_api.php | 98 ++++++++++++++++++++++++ web/skins/classic/views/options.php | 74 +----------------- 2 files changed, 99 insertions(+), 73 deletions(-) create mode 100644 web/skins/classic/views/_options_api.php diff --git a/web/skins/classic/views/_options_api.php b/web/skins/classic/views/_options_api.php new file mode 100644 index 000000000..6b975859d --- /dev/null +++ b/web/skins/classic/views/_options_api.php @@ -0,0 +1,98 @@ +APIs are disabled. To enable, please turn on OPT_USE_API in Options->System
'; + return; +} +?> + +
+ + + +
+'.translate('AllTokensRevoked').''; + } + + function updateSelected() { + # Turn them all off, then selectively turn the checked ones back on + dbQuery('UPDATE `Users` SET `APIEnabled`=0'); + + if (isset($_REQUEST['tokenUids'])) { + $minTime = time(); + foreach ($_REQUEST['tokenUids'] as $markUid) { + dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); + } + } + if (isset($_REQUEST['apiUids'])) { + foreach ($_REQUEST['apiUids'] as $markUid) { + dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); + } + } + echo ''.translate('Updated').''; + } + + if (array_key_exists('revokeAllTokens', $_POST)) { + revokeAllTokens(); + } + + if (array_key_exists('updateSelected', $_POST)) { + updateSelected(); + } +?> +

+ + + + + + + + + + + + +'Username']) as $u) { +?> + + + + + + + +
Username()) ?>APIEnabled()?'checked':''?> />
+
diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 9b9db9a03..15e1382c8 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -332,79 +332,7 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $ APIs are disabled. To enable, please turn on OPT_USE_API in Options->System'; - } else { -?> - -
- - -
-'.translate('AllTokensRevoked').''; - } - - function updateSelected() { - # Turn them all off, then selectively turn the checked ones back on - dbQuery('UPDATE `Users` SET `APIEnabled`=0'); - - if (isset($_REQUEST['tokenUids'])) { - foreach ($_REQUEST['tokenUids'] as $markUid) { - $minTime = time(); - dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); - } - } - if (isset($_REQUEST['apiUids'])) { - foreach ($_REQUEST['apiUids'] as $markUid) { - dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); - } - } - echo ''.translate('Updated').''; - } - - if (array_key_exists('revokeAllTokens', $_POST)) { - revokeAllTokens(); - } - - if (array_key_exists('updateSelected', $_POST)) { - updateSelected(); - } -?> -

- - - - - - - - - - - - - - - - - - - - -
/>
-
- Date: Fri, 13 Jan 2023 13:42:16 -0500 Subject: [PATCH 13/43] use ajax() instead of getJSON so we can specify no timeout --- web/skins/classic/views/js/montagereview.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 777a2aae5..802361f07 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -864,7 +864,7 @@ function click_all_events() { function allnon() { clicknav(0, 0, 0); } -/// >>>>>>>>>>>>>>>>> handles packing different size/aspect monitors on screen <<<<<<<<<<<<<<<<<<<<<<<< +/// handles packing different size/aspect monitors on screen function compSize(a, b) { // sort array by some size parameter - height seems to work best. A semi-greedy algorithm var a_value = monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a]; @@ -1221,8 +1221,10 @@ window.addEventListener('DOMContentLoaded', initPage); function load_Frames(zm_event) { return new Promise(function(resolve, reject) { - $j.getJSON(Servers[serverId].urlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay) - .done(function(data) { + $j.ajax(Servers[serverId].UrlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay, + { + timeout: 0, + success: function(data) { if (data.frames.length) { /* const zm_event = events[data.frames[0].Frame.EventId]; @@ -1250,15 +1252,16 @@ function load_Frames(zm_event) { zm_event.FramesById[frame.Id] = frame; } // end fireach frame - } // end if there are frames + } // end if there are frames drawGraph(); resolve(); - }) - .fail(function() { + }, + error: function() { logAjaxFail; reject(Error("There was an error")); } - ); - } // end Promise + } + ); // end ajax + } // end Promise ); } // end function load_Frames(Event) From d059806573981b6e9356dfe686db26fae812ff12 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Jan 2023 13:50:05 -0500 Subject: [PATCH 14/43] fix UrlToApi => urlToApi --- web/skins/classic/views/js/montagereview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 802361f07..403ca2982 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -1221,7 +1221,7 @@ window.addEventListener('DOMContentLoaded', initPage); function load_Frames(zm_event) { return new Promise(function(resolve, reject) { - $j.ajax(Servers[serverId].UrlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay, + $j.ajax(Servers[serverId].urlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay, { timeout: 0, success: function(data) { From 3cf8954878d010ccf58b7ead59c7ea7ff7e77c94 Mon Sep 17 00:00:00 2001 From: bradnewfield <86028786+bradnewfield@users.noreply.github.com> Date: Tue, 17 Jan 2023 06:08:51 +1300 Subject: [PATCH 15/43] Corrected code block syntax --- docs/installationguide/ubuntu.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index dd7a1f6e6..772e59cfa 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -30,6 +30,7 @@ To use this repository instead of the official Ubuntu repository, enter the foll **Step 3:** Install Zoneminder :: + sudo apt install -y zoneminder From ab2769ea4801aaf026c46e544e8ebe22e581cc42 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Jan 2023 16:37:33 -0500 Subject: [PATCH 16/43] Bulk load frames instead of by individual event --- web/skins/classic/views/js/montagereview.js | 149 +++++++++++--------- 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 403ca2982..7cc1c239b 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -176,7 +176,7 @@ function getFrame(monId, time, last_Frame) { if (!Event.FramesById) { console.log('No FramesById for event ', Event.Id); - load_Frames(Event).then(function() { + load_Frames([Event]).then(function() { if (!Event.FramesById) { console.log("No FramesById after load_Frames!", Event); } @@ -223,30 +223,30 @@ function getFrame(monId, time, last_Frame) { // time is seconds since epoch function getImageSource(monId, time) { - if ( liveMode == 1 ) { - var new_url = monitorImageObject[monId].src.replace( + if (liveMode == 1) { + let new_url = monitorImageObject[monId].src.replace( /rand=\d+/i, 'rand='+Math.floor(Math.random() * 1000000) ); - if ( auth_hash ) { + if (auth_hash) { // update auth hash new_url = new_url.replace(/auth=[a-z0-9]+/i, 'auth='+auth_hash); } return new_url; } - var frame_id; + let frame_id; - var Frame = getFrame(monId, time); - if ( Frame ) { + const Frame = getFrame(monId, time); + if (Frame) { + const e = events[Frame.EventId]; // Adjust for bulk frames - if ( Frame.NextFrameId ) { - var e = events[Frame.EventId]; - var NextFrame = e.FramesById[Frame.NextFrameId]; - if ( !NextFrame ) { + if (Frame.NextFrameId) { + const NextFrame = e.FramesById[Frame.NextFrameId]; + if (!NextFrame) { console.log("No next frame for " + Frame.NextFrameId); - } else if ( NextFrame.Type == 'Bulk' ) { + } else if (NextFrame.Type == 'Bulk') { // There is time between this frame and a bulk frame - var duration = Frame.NextTimeStampSecs - Frame.TimeStampSecs; + const duration = Frame.NextTimeStampSecs - Frame.TimeStampSecs; frame_id = Frame.FrameId + parseInt( (NextFrame.FrameId-Frame.FrameId) * ( time-Frame.TimeStampSecs )/duration ); //console.log("Have NextFrame: duration: " + duration + " frame_id = " + frame_id + " from " + NextFrame.FrameId + ' - ' + Frame.FrameId + " time: " + (time-Frame.TimeStampSecs) ); } else { @@ -255,24 +255,18 @@ function getImageSource(monId, time) { } else { frame_id = Frame.FrameId; } - Event = events[Frame.EventId]; - var storage = Storage[Event.StorageId]; - if ( !storage ) { - // Storage[0] is guaranteed to exist as we make sure it is there in montagereview.js.php - console.log("No storage area for id " + Event.StorageId); - storage = Storage[0]; - } + // Storage[0] is guaranteed to exist as we make sure it is there in montagereview.js.php + const storage = Storage[e.StorageId] ? Storage[e.StorageId] : Storage[0]; // monitorServerId may be 0, which gives us the default Server entry - var server = storage.ServerId ? Servers[storage.ServerId] : Servers[monitorServerId[monId]]; + const server = storage.ServerId ? Servers[storage.ServerId] : Servers[monitorServerId[monId]]; return server.PathToIndex + '?view=image&eid=' + Frame.EventId + '&fid='+frame_id + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; } // end found Frame return ''; - //return "no data"; -} +} // end function getImageSource // callback when loading an image. Will load itself to the canvas, or draw no data function imagedone( obj, monId, success ) { @@ -536,7 +530,7 @@ function drawGraph() { var x1=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth); // round low end down var x2=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ); // round up if (x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide - ctx.fillStyle=monitorColour[Event.MonitorId]; + //ctx.fillStyle=monitorColour[Event.MonitorId]; ctx.globalAlpha = 0.4 + 0.6 * (1 - Frame.Score/maxScore); // Background is scaled but even lowest is twice as dark as the background ctx.fillRect(x1, monitorIndex[Event.MonitorId]*rowHeight, x2-x1, rowHeight); } // end foreach frame @@ -1093,11 +1087,8 @@ function initPage() { }); }); - - if ( !liveMode ) { - for (const event_id in events) { - load_Frames(events[event_id]); - } + if (!liveMode) { + load_Frames(events); canvas = document.getElementById('timeline'); canvas.addEventListener('mousemove', mmove, false); @@ -1219,49 +1210,67 @@ window.addEventListener("resize", redrawScreen, {passive: true}); // Kick everything off window.addEventListener('DOMContentLoaded', initPage); -function load_Frames(zm_event) { +function load_Frames(zm_events) { + console.log(zm_events); + return new Promise(function(resolve, reject) { - $j.ajax(Servers[serverId].urlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay, - { - timeout: 0, - success: function(data) { - if (data.frames.length) { - /* - const zm_event = events[data.frames[0].Frame.EventId]; - if (!zm_event) { - console.error("No event object found for " + data.frames[0].Frame.EventId); - reject(Error("There was an error")); - return; + let url = Servers[serverId].urlToApi()+'/frames/index'; + console.log(Array.isArray(zm_events), zm_events.length, typeof(zm_events)); + + let query = ''; + let ids = Object.keys(zm_events); + + while (ids.length) { + const event_id = ids.shift(); + const zm_event = zm_events[event_id]; + console.log(zm_event); + + query += '/EventId:'+zm_event.Id; + if (!ids.length || (query.length > 1000)) { + console.log(url,query, url+query+'.json?'+auth_relay); + $j.ajax(url+query+'.json?'+auth_relay, + { + timeout: 0, + success: function(data) { + if (data.frames.length) { + zm_event.FramesById = []; + let last_frame = null; + + for (let i=0, len=data.frames.length; i Date: Fri, 13 Jan 2023 16:45:13 -0500 Subject: [PATCH 17/43] Remove frames loading code. We do it via api now. --- .../classic/views/js/montagereview.js.php | 30 ++----------------- web/skins/classic/views/montagereview.php | 29 ------------------ 2 files changed, 3 insertions(+), 56 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index e243c6c0d..018ef613c 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -22,7 +22,6 @@ global $minTime; global $maxTime; global $monitors; global $eventsSql; -global $framesSql; ?> var currentScale=; @@ -64,31 +63,8 @@ if (!$liveMode) { $EventsById = array(); while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) { - $event_id = $event['Id']; - $EventsById[$event_id] = $event; + $EventsById[$event['Id']] = $event; } - $next_frames = array(); -if ( 0 ) { - if ( $result = dbQuery($framesSql) ) { - $next_frame = null; - while ( $frame = $result->fetch(PDO::FETCH_ASSOC) ) { - $event_id = $frame['EventId']; - $event = &$EventsById[$event_id]; - - $frame['TimeStampSecs'] = $event['StartTimeSecs'] + $frame['Delta']; - if ( !isset($event['FramesById']) ) { - // Please note that this is the last frame as we sort DESC - $event['FramesById'] = array(); - $frame['NextTimeStampSecs'] = $event['EndTimeSecs']; - } else { - $frame['NextTimeStampSecs'] = $next_frames[$frame['EventId']]['TimeStampSecs']; - $frame['NextFrameId'] = $next_frames[$frame['EventId']]['Id']; - } - $event['FramesById'] += array($frame['Id']=>$frame); - $next_frames[$frame['EventId']] = &$event['FramesById'][$frame['Id']]; - } - } // end if dbQuery -} $events_by_monitor_id = array(); @@ -111,8 +87,8 @@ if ( 0 ) { $maxScore = $event['MaxScore']; $anyAlarms = true; } - if ( !isset($events_by_monitor_id[$event['MonitorId']]) ) - $events_by_monitor_id[$event['MonitorId']] = array(); + if (!isset($events_by_monitor_id[$event['MonitorId']])) + $events_by_monitor_id[$event['MonitorId']] = array(); array_push($events_by_monitor_id[$event['MonitorId']], $event_id); } # end foreach Event echo ' }; diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index cc55d2208..0a9a09a8e 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -159,30 +159,15 @@ $eventsSql = 'SELECT WHERE 1 > 0 '; -// select E.Id,E.Name,UNIX_TIMESTAMP(E.StartDateTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartDateTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId -// from Events as E -// inner join Monitors as M on (E.MonitorId = M.Id) -// inner join Frames F on F.EventId=E.Id -// where not isnull(E.Frames) and not isnull(StartDateTime) "; - -// Note that the delta value seems more accurate than the time stamp for some reason. -$framesSql = ' - SELECT Id, FrameId, EventId, TimeStamp, UNIX_TIMESTAMP(TimeStamp) AS TimeStampSecs, Score, Delta, Type - FROM Frames - WHERE EventId IN (SELECT E.Id FROM Events AS E WHERE 1>0 -'; - // This program only calls itself with the time range involved -- it does all monitors (the user can see, in the called group) all the time $monitor_ids_sql = ''; if ( !empty($user['MonitorIds']) ) { $eventsSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')'; - $framesSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')'; } if ( count($selected_monitor_ids) ) { $monitor_ids_sql = ' IN (' . implode(',',$selected_monitor_ids).')'; $eventsSql .= ' AND E.MonitorId '.$monitor_ids_sql; - $framesSql .= ' AND E.MonitorId '.$monitor_ids_sql; } if ( isset($_REQUEST['archive_status']) ) { $_SESSION['archive_status'] = $_REQUEST['archive_status']; @@ -190,14 +175,11 @@ if ( isset($_REQUEST['archive_status']) ) { if ( isset($_SESSION['archive_status']) ) { if ( $_SESSION['archive_status'] == 'Archived' ) { $eventsSql .= ' AND E.Archived=1'; - $framesSql .= ' AND E.Archived=1'; } else if ( $_SESSION['archive_status'] == 'Unarchived' ) { $eventsSql .= ' AND E.Archived=0'; - $framesSql .= ' AND E.Archived=0'; } } - $fitMode = 1; if ( isset($_REQUEST['fit']) && ($_REQUEST['fit'] == '0') ) $fitMode = 0; @@ -222,7 +204,6 @@ for ( $i = 0; $i < count($speeds); $i++ ) { } } - $liveMode = 1; // default to live if ( isset($_REQUEST['live']) && ($_REQUEST['live'] == '0') ) $liveMode = 0; @@ -231,8 +212,6 @@ $initialDisplayInterval = 1000; if ( isset($_REQUEST['displayinterval']) ) $initialDisplayInterval = validHtmlStr($_REQUEST['displayinterval']); -#$eventsSql .= ' GROUP BY E.Id,E.Name,E.StartDateTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId'; - $minTimeSecs = $maxTimeSecs = 0; if ( isset($minTime) && isset($maxTime) ) { if ($minTime >= $maxTime) { @@ -246,16 +225,8 @@ if ( isset($minTime) && isset($maxTime) ) { $minTimeSecs = strtotime($minTime); $maxTimeSecs = strtotime($maxTime); $eventsSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; - $framesSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; - $framesSql .= ") AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'"; -} else { - $framesSql .= ')'; } -#$framesSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC'; -#$framesSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC'; $eventsSql .= ' ORDER BY E.Id ASC'; -// DESC is intentional. We process them in reverse order so that we can point each frame to the next one in time. -$framesSql .= ' ORDER BY Id DESC'; $monitors = array(); foreach ($displayMonitors as $row) { From 0af2a3eb10e7eef92f815a4ccf1b99fce81602e9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 14 Jan 2023 12:41:29 -0500 Subject: [PATCH 18/43] Make header navbar sticky, make entire second navbar go away when minmised. --- web/skins/classic/css/base/skin.css | 7 +++---- web/skins/classic/includes/functions.php | 6 ++++-- web/skins/classic/js/skin.js | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 58be658d6..88e319fc3 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -603,6 +603,9 @@ input[type=submit]:disabled, #navbar-container { padding: 0; + position: sticky; + top: 0; + z-index: 1000; } .navbar{ @@ -629,10 +632,6 @@ color:#ffa801; border:none; } -.container-fluid { - position: relative; -} - .sidebar { position: absolute; top: 0; diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 2f262c8ce..912949b9d 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -248,8 +248,10 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin) -