From 8fbce6e47bc91aead90224f4f6568d2ef282dbe4 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Thu, 24 Nov 2022 17:59:14 +0200 Subject: [PATCH 1/4] Add video browser api --- camera.js | 2 + libs/videobrowser.js | 278 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 libs/videobrowser.js diff --git a/camera.js b/camera.js index 19ca4263..95a800af 100644 --- a/camera.js +++ b/camera.js @@ -95,4 +95,6 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => { require('./libs/commander.js')(s,config,lang,app) //cron require('./libs/cron.js')(s,config,lang) + //video browser functions + require('./libs/videobrowser.js')(s,config,lang) }) diff --git a/libs/videobrowser.js b/libs/videobrowser.js new file mode 100644 index 00000000..3f513ad5 --- /dev/null +++ b/libs/videobrowser.js @@ -0,0 +1,278 @@ +const knex = require('knex'); +const moment = require('moment'); + +const ENDPOINT_SECTION = "videobrowser"; + +module.exports = (s, shinobiConfig, lang, app, io) => { + const instance = { + db: null, + timeZoneOffest: 0 + }; + + const setOkResponse = (response, data, message) => { + console.debug(message); + + response.send({ + ok: true, + data: data + }); + }; + + const setErrorResponse = (response, error, flowName) => { + console.error(`Failed to ${flowName}, Error: ${error}`); + + response.status(500).send(); + }; + + const getISODateTime = (date) => { + const dateTime = moment(date).add(instance.timeZoneOffest, "minutes"); + const dateTimeISO = dateTime.toISOString(); + const dateTimeISOParts = dateTimeISO.split("."); + const result = dateTimeISOParts[0]; + + return result; + }; + + const getISODate = (dateStr) => { + const dateTimeISO = getISODateTime(dateStr); + const dateTimeISOParts = dateTimeISO.split("T"); + const dateISO = dateTimeISOParts[0]; + + return dateISO; + }; + + const fixItemsDate = (dataItems) => { + dataItems.forEach(i => { + i.date = getISODate(i.date); + }); + }; + + const getMonitorsList = (requestParams, response) => { + const groupKey = requestParams.ke; + + instance.db + .select('ke', 'mid') + .from('Videos') + .where('ke', '=', groupKey) + .groupBy('ke') + .groupBy('mid') + .then(data => { + setOkResponse( + response, + data, + `Reterived ${data.length} monitors with videos, Group ID: ${groupKey}` + ); + + }) + .catch(error => { + setErrorResponse( + response, + error, + `reterive monitors list, Group ID: ${groupKey}` + ); + }); + }; + + const setResponseForMonitorDatesList = (response, videoItems, imageItems) => { + const firstVideoItem = videoItems[0]; + const groupKey = firstVideoItem.ke; + const monitorId = firstVideoItem.mid; + + fixItemsDate(imageItems); + + videoItems.forEach(v => { + const imagesOfVideo = imageItems.filter(i => i.date === v.date); + + if (imagesOfVideo.length > 0) { + const chosenImage = imagesOfVideo[0]; + + v.filename = chosenImage.filename; + } + }); + + setOkResponse( + response, + videoItems, + `Reterived ${videoItems.length} videos and ${imageItems.length} images, Group ID: ${groupKey}, Monitor ID: ${monitorId}` + ); + }; + + const loadImagesForMonitorDatesList = (response, videoItems) => { + const firstVideoItem = videoItems[0]; + const groupKey = firstVideoItem.ke; + const monitorId = firstVideoItem.mid; + + fixItemsDate(videoItems); + + instance.db + .select('ke', 'mid', 'filename', instance.db.raw("CAST(time AS DATE) as date")) + .from('Timelapse Frames') + .where('ke', '=', groupKey) + .andWhere('mid', '=', monitorId) + .groupBy('ke') + .groupBy('mid') + .groupByRaw('CAST(time AS DATE)') + .orderBy("time", "desc") + .then(imageItems => { + setResponseForMonitorDatesList(response, videoItems, imageItems); + }) + .catch(error => { + setErrorResponse( + response, + error, + `reterive monitor dates list, Group ID: ${groupKey}, Monitor ID: ${monitorId}` + ); + }); + }; + + const getMonitorDatesList = (requestParams, response) => { + const groupKey = requestParams.ke; + const monitorId = requestParams.id; + + instance.db + .select('ke', 'mid', instance.db.raw("CAST(time AS DATE) as date")) + .from('Videos') + .where('ke', '=', groupKey) + .andWhere('mid', '=', monitorId) + .groupBy('ke') + .groupBy('mid') + .groupByRaw('CAST(time AS DATE)') + .orderBy("time", "desc") + .then(videoItems => { + loadImagesForMonitorDatesList(response, videoItems); + }) + .catch(error => { + setErrorResponse( + response, + error, + `reterive monitor dates list, Group ID: ${groupKey}, Monitor ID: ${monitorId}` + ); + }); + }; + + const setResponseForMonitorVideosList = (response, videoItems, imageItems) => { + const firstVideoItem = videoItems[0]; + const monitorId = firstVideoItem.mid; + const videoDate = getISODate(firstVideoItem.date); + + videoItems.forEach(v => { + const imagesOfVideo = imageItems.filter(i => i.time >= v.time && i.time <= v.end); + + if (imagesOfVideo.length > 0) { + const chosenImage = imagesOfVideo[0]; + + v.time = getISODateTime(v.time); + v.filename = chosenImage.filename; + + delete v.end; + } + }); + + setOkResponse( + response, + videoItems, + `Reterived ${videoItems.length} videos and ${imageItems.length} images, Monitor ID: ${monitorId}, Date: ${videoDate}` + ); + }; + + const loadImagesForMonitorVideosList = (requestParams, response, videoItems) => { + const groupKey = requestParams.ke; + const monitorId = requestParams.id; + const videoDate = requestParams.date; + + instance.db + .select('ke', 'mid', 'time', 'filename') + .from('Timelapse Frames') + .where('ke', '=', groupKey) + .andWhere('mid', '=', monitorId) + .andWhere('time', '>=', `${videoDate} 00:00:00.000`) + .andWhere('time', '<=', `${videoDate} 23:59:59.999`) + .orderBy("time", "desc") + .then(imageItems => { + setResponseForMonitorVideosList(response, videoItems, imageItems); + }) + .catch(error => { + setErrorResponse( + response, + error, + `reterive monitor videos list, Group ID: ${groupKey}, Monitor ID: ${monitorId}, Date: ${videoDate}` + ); + }); + }; + + const getMonitorVideosList = (requestParams, response) => { + const groupKey = requestParams.ke; + const monitorId = requestParams.id; + const videoDate = requestParams.date; + + instance.db + .select('ke', 'mid', 'time', 'end', 'ext') + .from('Videos') + .where('ke', '=', groupKey) + .andWhere('mid', '=', monitorId) + .andWhere('time', '>=', `${videoDate} 00:00:00.000`) + .andWhere('time', '<=', `${videoDate} 23:59:59.999`) + .orderBy("time", "desc") + .then(videoItems => { + loadImagesForMonitorVideosList(requestParams, response, videoItems); + }) + .catch(error => { + setErrorResponse( + response, + error, + `reterive monitor videos list, Group ID: ${groupKey}, Monitor ID: ${monitorId}, Date: ${videoDate}` + ); + }); + }; + + const initialize = () => { + const apiPrefix = `${shinobiConfig.webPaths.apiPrefix}:auth/${ENDPOINT_SECTION}`; + + const apiSettings = [ + { + suffix: `/:ke`, + callback: getMonitorsList + }, + { + suffix: `/:ke/:id`, + callback: getMonitorDatesList + }, + { + suffix:`/:ke/:id/:date`, + callback: getMonitorVideosList + } + ]; + + apiSettings.forEach(endpointSettings => { + const endpointSuffix = endpointSettings.suffix; + const endpointCallback = endpointSettings.callback; + + const endpointRoute = `${apiPrefix}${endpointSuffix}` + + app.get(endpointRoute, (req, res) => { + s.auth(req.params, user => { + endpointCallback(req.params, res); + }, res, req); + }); + }); + + const dbConfig = { + client: shinobiConfig.databaseType, + connection: shinobiConfig.db + }; + + if(dbConfig.client.indexOf('sqlite')>-1){ + dbConfig.client = 'sqlite3'; + dbConfig.useNullAsDefault = true; + } + + if(dbConfig.client === 'sqlite3' && dbConfig.connection.filename === undefined){ + dbConfig.connection.filename = `${__dirname}/shinobi.sqlite`; + } + + instance.db = knex(dbConfig); + instance.timeZoneOffest = new Date().getTimezoneOffset() * -1; + }; + + initialize(); +}; From 82a84dd4100c0d864852ae7d88c92e39b0a2e63c Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Thu, 24 Nov 2022 18:26:22 +0200 Subject: [PATCH 2/4] updated docker files to support node v16 --- Dockerfile | 2 +- Dockerfile.arm32v7 | 2 +- Dockerfile.nvidia | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e0b40bde..cb1a591f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13-buster-slim +FROM node:16-buster-slim ENV DB_USER=majesticflame \ DB_PASSWORD='' \ diff --git a/Dockerfile.arm32v7 b/Dockerfile.arm32v7 index c85896f6..aa7c6aa9 100644 --- a/Dockerfile.arm32v7 +++ b/Dockerfile.arm32v7 @@ -1,4 +1,4 @@ -FROM arm32v7/node:12.18.3-buster-slim +FROM arm32v7/node:16-buster-slim ENV DB_USER=majesticflame \ DB_PASSWORD='' \ diff --git a/Dockerfile.nvidia b/Dockerfile.nvidia index 10ac76ef..b2b7ff65 100644 --- a/Dockerfile.nvidia +++ b/Dockerfile.nvidia @@ -1,4 +1,4 @@ -FROM nvidia/cuda:11.4.2-cudnn8-runtime-ubuntu20.04 +FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu20.04 ENV DB_USER=majesticflame \ DB_PASSWORD='' \ From 6ad2c172ccaf1c2c3a825d203d2beeae49749b09 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Thu, 24 Nov 2022 20:38:47 +0200 Subject: [PATCH 3/4] add missing parameter of app (express web application instance) --- camera.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera.js b/camera.js index 95a800af..f7184efc 100644 --- a/camera.js +++ b/camera.js @@ -96,5 +96,5 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => { //cron require('./libs/cron.js')(s,config,lang) //video browser functions - require('./libs/videobrowser.js')(s,config,lang) + require('./libs/videobrowser.js')(s,config,lang,app,io) }) From e4b9b9bb35ec06ac51e27ca70fad9fca451e57c6 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Thu, 24 Nov 2022 20:40:43 +0200 Subject: [PATCH 4/4] renamed videobrowser to videoBrowser --- camera.js | 2 +- libs/videobrowser.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/camera.js b/camera.js index f7184efc..a5df52fd 100644 --- a/camera.js +++ b/camera.js @@ -96,5 +96,5 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => { //cron require('./libs/cron.js')(s,config,lang) //video browser functions - require('./libs/videobrowser.js')(s,config,lang,app,io) + require('./libs/videoBrowser.js')(s,config,lang,app,io) }) diff --git a/libs/videobrowser.js b/libs/videobrowser.js index 3f513ad5..5d0ea634 100644 --- a/libs/videobrowser.js +++ b/libs/videobrowser.js @@ -1,7 +1,7 @@ const knex = require('knex'); const moment = require('moment'); -const ENDPOINT_SECTION = "videobrowser"; +const ENDPOINT_SECTION = "videoBrowser"; module.exports = (s, shinobiConfig, lang, app, io) => { const instance = {