diff --git a/www/controllers/Service/Unit/Timelapse.php b/www/controllers/Service/Unit/Timelapse.php index 6c3bf4c2..f412cf58 100644 --- a/www/controllers/Service/Unit/Timelapse.php +++ b/www/controllers/Service/Unit/Timelapse.php @@ -25,9 +25,6 @@ public function run(): void { parent::log('Starting timelapse'); - // Get list of cameras - $cameras = $this->cameraController->get(); - while (true) { // Always retrieve timelapse interval before running, in case it has been changed by the user $timelapseInterval = parent::getSettings('Timelapse_interval'); @@ -44,6 +41,9 @@ public function run(): void sleep($nextInterval - time()); } + // Get list of cameras + $cameras = $this->cameraController->get(); + // For each camera, if timelapse is enabled, execute timelapse foreach ($cameras as $camera) { try { @@ -73,7 +73,7 @@ public function run(): void parent::log('Capture timelapse for camera #' . $camera['Id'] . ' (' . $configuration['name'] . ')'); - $myprocess = new \Controllers\Process('/usr/bin/ffmpeg -rw_timeout 3000000 -loglevel error -i http://127.0.0.1:1984/api/frame.jpeg?src=camera_' . $camera['Id'] . ' -c:v libsvtav1 -crf 30 -preset 10 -threads 1 ' . $targetDir . '/timelapse_' . date('H-i-s') . '.avif'); + $myprocess = new \Controllers\Process('/usr/bin/ffmpeg -rw_timeout 3000000 -loglevel error -i http://127.0.0.1:1984/api/frame.jpeg?src=camera_' . $camera['Id'] . ' -c:v libsvtav1 -crf 30 -preset 10 ' . $targetDir . '/timelapse_' . date('H-i-s') . '.avif'); $myprocess->execute(); $myprocess->close(); @@ -86,6 +86,7 @@ public function run(): void } } + // Just for safety sleep(1); } } diff --git a/www/public/resources/js/classes/EChart.js b/www/public/resources/js/classes/EChart.js index 8f9a871b..9f7ec30c 100644 --- a/www/public/resources/js/classes/EChart.js +++ b/www/public/resources/js/classes/EChart.js @@ -1171,13 +1171,16 @@ class EChart // Set options and render chart.setOption(options); - // Force zoom reset if period changed + // Force zoom reset if period changed - this ensures consistent behavior if (this._periodChanged) { - chart.dispatchAction({ - type: 'dataZoom', - start: 0, - end: 100 - }); + // Add a small delay to ensure the chart is fully rendered + setTimeout(() => { + chart.dispatchAction({ + type: 'dataZoom', + start: 0, + end: 100 + }); + }, 50); } // Remove spinner @@ -1376,18 +1379,28 @@ EChart.destroyInstance = function(chartId) { * @param {*} id * @param {*} autoUpdate * @param {*} autoUpdateInterval - * @param {*} days + * @param {*} providedDays - Number of days for the chart. If null, preserves the current chart's days value. */ -EChart.recreate = function(type, id, autoUpdate = true, autoUpdateInterval = 15000, days = 1) { +EChart.recreate = function(type, id, autoUpdate = true, autoUpdateInterval = 15000, providedDays = null) { // Check if existing chart was in natural state before destroying let wasInNaturalState = true; let periodChanged = false; let preservedCurrentType = type; // Default to original type let instance = null; // Declare instance variable + let days = providedDays; // The final days value to use try { instance = EChart.instances[id]; + // If no days specified, try to preserve current chart's days value + if (providedDays === null && instance) { + days = instance.days; + console.info('EChart.recreate: preserving current days value:', days); + } else if (providedDays === null) { + // Fallback if no instance exists + days = 1; + } + // Get current chart element for zoom state check const chartElement = document.querySelector("#" + id); if (chartElement && chartElement._chartInstance && instance) { @@ -1396,19 +1409,29 @@ EChart.recreate = function(type, id, autoUpdate = true, autoUpdateInterval = 150 wasInNaturalState = instance.isInNaturalState(currentOption.dataZoom); } - // Check if period has changed (using Number for comparison) + // Preserve the current chart type (may have been changed by magicType) + preservedCurrentType = instance.currentType || instance.type; + } + + // Check if period has changed - do this after days value is determined + if (instance) { const oldDays = Number(instance.days); const newDays = Number(days); periodChanged = oldDays !== newDays; - // Preserve the current chart type (may have been changed by magicType) - preservedCurrentType = instance.currentType || instance.type; + if (periodChanged) { + console.info('EChart.recreate: period changed from', oldDays, 'to', newDays, '- will reset zoom'); + } } } catch (error) { console.warn('EChart.recreate: Error checking zoom state, defaulting to natural state', error); wasInNaturalState = true; periodChanged = false; preservedCurrentType = type; + // Ensure we have a fallback days value + if (days === null) { + days = 1; + } } if (EChart.destroyInstance(id)) { @@ -1465,4 +1488,20 @@ $(document).ready(function () { // Destroy and recreate chart with new days value (pass as number) EChart.recreate(type, chartId, true, 15000, days); }); + + // Listen for changes on any select element with class 'echart-range' + // TODO: ranges + // $('select.echart-range').on('change', function() { + // // Get chart Id + // const chartId = $(this).attr('chart-id'); + + // // Get selected value - convert to number to match internal storage + // const range = Number($(this).val()); + + // // Get chart type from 'type' or 'chart-type' attribute, default to 'line' if not specified + // const type = $('#' + chartId).attr('type') || $('#' + chartId).attr('chart-type') || 'line'; + + // // Destroy and recreate chart with new days value (pass as number) + // EChart.recreate(type, chartId, true, 15000, days); + // }); }); diff --git a/www/public/resources/js/classes/Motion/Media.js b/www/public/resources/js/classes/Motion/Media.js index 7018fcc2..fcffe5ef 100644 --- a/www/public/resources/js/classes/Motion/Media.js +++ b/www/public/resources/js/classes/Motion/Media.js @@ -9,7 +9,7 @@ class Media // Get selected media Id $(checkboxes).each(function () { - files.push({ fileId: $(this).attr('file-id'), filename: $(this).attr('file-name') }); + files.push($(this).attr('file-name')); }); // Append a temporary element to download files @@ -19,12 +19,15 @@ class Media document.body.appendChild(temporaryDownloadLink); for (var n = 0; n < files.length; n++) { - var download = files[n]; + var path = files[n]; + var filename = path.split('/').pop(); + + // Set the href attribute to the file path, also include the filename as a query parameter for the Android app + temporaryDownloadLink.setAttribute('href', '/media/' + path + '?filename=' + encodeURIComponent(filename)); - // Set the href attribute to the file path, also include the filename for the android app to make sure it downloads the file with the correct name - temporaryDownloadLink.setAttribute('href', '/media?id=' + download.fileId + '&filename=' + download.filename); // Set the download attribute to force download - temporaryDownloadLink.setAttribute('download', download.filename); + temporaryDownloadLink.setAttribute('download', filename); + // Trigger click on the temporary element to start download temporaryDownloadLink.click(); } diff --git a/www/public/resources/js/motion.js b/www/public/resources/js/motion.js index 68c47cc8..be67b8b9 100644 --- a/www/public/resources/js/motion.js +++ b/www/public/resources/js/motion.js @@ -125,11 +125,9 @@ function eventDateSelect(dateStart, dateEnd) * Event: vizualize event image */ $(document).on('click','.play-picture-btn',function () { - var fileId = $(this).attr('file-id'); - html = '
' + '
' - + '' + + '' + '
' + '
' + '' diff --git a/www/public/resources/styles/components/input.css b/www/public/resources/styles/components/input.css index c3176ba0..f7fd1156 100644 --- a/www/public/resources/styles/components/input.css +++ b/www/public/resources/styles/components/input.css @@ -102,6 +102,27 @@ input[type=checkbox].checkbox-warning:not(.onoff-switch-input) { } } +.input-daterangepicker { + width: 250px; + height: 35px; + margin-top: 4px; + margin-bottom: 4px; + padding: 0 10px; + border: none; + border-radius: 20px; + box-sizing: border-box; + font-size: 14px; + color: white; + background-color:#22384F; +} + +.input-daterangepicker p { + line-height: 35px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + /* Textarea */ textarea { width: 100%; diff --git a/www/public/resources/styles/components/label.css b/www/public/resources/styles/components/label.css index e51b10e4..b61b9e1f 100644 --- a/www/public/resources/styles/components/label.css +++ b/www/public/resources/styles/components/label.css @@ -21,6 +21,7 @@ column-gap: 10px; font-size: 12px; padding: 4px 10px; + margin: 1px; min-width: 50px; min-height: 12px; border-radius: 60px; diff --git a/www/version b/www/version index ceda194c..c6823264 100644 --- a/www/version +++ b/www/version @@ -1 +1 @@ -6.11.0 \ No newline at end of file +6.11.1 \ No newline at end of file diff --git a/www/views/includes/camera/edit/motion-config-form.inc.php b/www/views/includes/camera/edit/motion-config-form.inc.php index e00f431d..bb5a42e5 100644 --- a/www/views/includes/camera/edit/motion-config-form.inc.php +++ b/www/views/includes/camera/edit/motion-config-form.inc.php @@ -56,7 +56,7 @@
-
+

diff --git a/www/views/includes/tables/motion/events.inc.php b/www/views/includes/tables/motion/events.inc.php index 855331aa..240d62f4 100644 --- a/www/views/includes/tables/motion/events.inc.php +++ b/www/views/includes/tables/motion/events.inc.php @@ -170,19 +170,19 @@ - +
-

#

+

#

'; + echo ''; } else { echo ''; } @@ -254,7 +254,7 @@ '; + echo ''; } else { echo ''; }