diff --git a/CHANGELOG.md b/CHANGELOG.md index b953a1c3..a464a4e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Changelog All notable changes to this project will be documented in this file. +# v1.1.12 - 2022-04-23 + +## Other Changes +- Improved probe stream +- Minor improvements +- Bump dependencies + +## Bugfixes +- Fixed an issue where recording information such as motion label was not correctly saved in the image data +- Fixed an issue where prebuffering and/or video analysis was started for disabled cameras anyway +- Minor bugfixes + # v1.1.11 - 2022-04-23 ## Notable Changes diff --git a/package-lock.json b/package-lock.json index 577a5f2c..b9390606 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "camera.ui", - "version": "1.1.11", + "version": "1.1.12-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "camera.ui", - "version": "1.1.11", + "version": "1.1.12-beta.1", "funding": [ { "type": "paypal", @@ -34,7 +34,7 @@ "connect-history-api-fallback": "^1.6.0", "cors": "^2.8.5", "express": "^4.17.3", - "ffmpeg-for-homebridge": "^0.0.9", + "ffmpeg-for-homebridge": "0.0.9", "fs-extra": "^10.1.0", "ftp-srv": "^4.6.0", "got": "^12.0.3", @@ -75,7 +75,7 @@ "eslint": "^8.13.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.1.4", + "eslint-plugin-jest": "^26.1.5", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-unicorn": "^42.0.0", "jest": "^27.5.1", @@ -4593,9 +4593,9 @@ "dev": true }, "node_modules/eslint-plugin-jest": { - "version": "26.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz", - "integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==", + "version": "26.1.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.5.tgz", + "integrity": "sha512-su89aDuljL9bTjEufTXmKUMSFe2kZUL9bi7+woq+C2ukHZordhtfPm4Vg+tdioHBaKf8v3/FXW9uV0ksqhYGFw==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -5334,20 +5334,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/ftp-srv": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/ftp-srv/-/ftp-srv-4.6.0.tgz", @@ -15006,9 +14992,9 @@ } }, "eslint-plugin-jest": { - "version": "26.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz", - "integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==", + "version": "26.1.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.5.tgz", + "integrity": "sha512-su89aDuljL9bTjEufTXmKUMSFe2kZUL9bi7+woq+C2ukHZordhtfPm4Vg+tdioHBaKf8v3/FXW9uV0ksqhYGFw==", "dev": true, "requires": { "@typescript-eslint/utils": "^5.10.0" @@ -15505,13 +15491,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "ftp-srv": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/ftp-srv/-/ftp-srv-4.6.0.tgz", diff --git a/package.json b/package.json index dac14481..bf4466bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "camera.ui", - "version": "1.1.11", + "version": "1.1.12-beta.1", "description": "NVR like user interface for RTSP capable cameras.", "author": "SeydX (https://github.com/SeydX/camera.ui)", "scripts": { @@ -27,7 +27,7 @@ "connect-history-api-fallback": "^1.6.0", "cors": "^2.8.5", "express": "^4.17.3", - "ffmpeg-for-homebridge": "^0.0.9", + "ffmpeg-for-homebridge": "0.0.9", "fs-extra": "^10.1.0", "ftp-srv": "^4.6.0", "got": "^12.0.3", @@ -65,7 +65,7 @@ "eslint": "^8.13.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.1.4", + "eslint-plugin-jest": "^26.1.5", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-unicorn": "^42.0.0", "jest": "^27.5.1", diff --git a/src/api/components/notifications/notifications.model.js b/src/api/components/notifications/notifications.model.js index f51be401..efdd162e 100644 --- a/src/api/components/notifications/notifications.model.js +++ b/src/api/components/notifications/notifications.model.js @@ -105,13 +105,12 @@ export const createNotification = async (data) => { const cameraSetting = camerasSettings.find((cameraSetting) => cameraSetting && cameraSetting.name === camera.name); const id = data.id || (await nanoid()); - const cameraName = camera.name; const room = cameraSetting ? cameraSetting.room : 'Standard'; const timestamp = data.timestamp || moment().unix(); const time = moment.unix(timestamp).format('YYYY-MM-DD HH:mm:ss'); const fileName = - cameraName.replace(/\s+/g, '_') + + camera.name.replace(/\s+/g, '_') + '-' + id + '-' + @@ -124,7 +123,7 @@ export const createNotification = async (data) => { const notification = { id: id, - camera: cameraName, + camera: camera.name, fileName: `${fileName}.${extension}`, name: fileName, extension: extension, @@ -139,7 +138,7 @@ export const createNotification = async (data) => { const notify = { ...notification, - title: cameraName, + title: camera.name, message: `${data.trigger} - ${time}`, subtxt: room, mediaSource: data.storing ? `/files/${fileName}.${extension}` : false, diff --git a/src/api/components/recordings/recordings.model.js b/src/api/components/recordings/recordings.model.js index c54948c0..299587fe 100644 --- a/src/api/components/recordings/recordings.model.js +++ b/src/api/components/recordings/recordings.model.js @@ -113,6 +113,7 @@ export const createRecording = async (data, fileBuffer) => { const cameraSetting = camerasSettings.find((cameraSetting) => cameraSetting && cameraSetting.name === camera.name); const id = data.id || (await nanoid()); + const room = cameraSetting ? cameraSetting.room : 'Standard'; const timestamp = data.timestamp || moment().unix(); const time = moment.unix(timestamp).format('YYYY-MM-DD HH:mm:ss'); @@ -137,7 +138,7 @@ export const createRecording = async (data, fileBuffer) => { recordStoring: true, recordType: data.type, trigger: data.trigger, - room: cameraSetting.room, + room: room, time: time, timestamp: timestamp, label: label, @@ -145,7 +146,7 @@ export const createRecording = async (data, fileBuffer) => { if (fileBuffer) { await storeVideoBuffer(camera, fileBuffer, data.path, fileName); - await storeSnapshotFromVideo(camera, data.path, fileName); + await storeSnapshotFromVideo(camera, data.path, fileName, label); } else { const isPlaceholder = data.type === 'Video'; const externRecording = false; diff --git a/src/api/database.js b/src/api/database.js index 10680fc7..18f80457 100644 --- a/src/api/database.js +++ b/src/api/database.js @@ -310,7 +310,7 @@ export default class Database { let extension = isPlaceholder ? 'mp4' : 'jpeg'; let id = rec.match(/(?<=-)([\dA-z]{10})(?=-)/)[0]; let timestamp = rec.match(/(?<=-)(\d{10})(?=_)/)[0]; - let cameraName = rec.match(/(.*?)(?=-[\dA-z]{10})/)[0]; + let cameraName = rec.match(/(.*?)(?=-[\dA-z]{10})/)[0]?.replace(/_/g, ' '); let cameraSetting = cameras.find((camera) => camera?.name === cameraName); const jpeg = fs.readFileSync(filePath); @@ -327,7 +327,7 @@ export default class Database { return { id: id, - camera: cameraName.replace(/_/g, ' '), + camera: cameraName, fileName: `${fileName}.${extension}`, name: fileName, extension: extension, diff --git a/src/common/ffmpeg.js b/src/common/ffmpeg.js index e660014e..9950e9f7 100644 --- a/src/common/ffmpeg.js +++ b/src/common/ffmpeg.js @@ -242,7 +242,7 @@ export const getAndStoreSnapshot = ( }); }; -export const storeSnapshotFromVideo = async (camera, recordingPath, fileName) => { +export const storeSnapshotFromVideo = async (camera, recordingPath, fileName, label) => { return new Promise((resolve, reject) => { const videoProcessor = ConfigService.ui.options.videoProcessor; const videoName = `${recordingPath}/${fileName}.mp4`; @@ -282,6 +282,9 @@ export const storeSnapshotFromVideo = async (camera, recordingPath, fileName) => reject(new Error(errors.join(' - '))); } else { log.debug('FFmpeg snapshot process exited (expected)', camera.name, 'ffmpeg'); + + replaceJpegWithExifJPEG(camera.name, destination, label); + resolve(); } }); diff --git a/src/controller/camera/camera.controller.js b/src/controller/camera/camera.controller.js index 0907249a..32dee060 100644 --- a/src/controller/camera/camera.controller.js +++ b/src/controller/camera/camera.controller.js @@ -79,6 +79,10 @@ export default class CameraController { await controller.media.probe(); + if (controller.options?.disable) { + return; + } + if (controller.options?.prebuffering) { await controller.prebuffer.start(); } @@ -87,7 +91,7 @@ export default class CameraController { await controller.videoanalysis.start(); } - await controller.stream.configureStreamOptions(); + //await controller.stream.configureStreamOptions(); } static async reconfigureController(cameraName) { @@ -97,6 +101,10 @@ export default class CameraController { throw new Error(`Can not reconfigure controller, controller for ${cameraName} not found!`); } + if (camera.disable) { + return; + } + const camera = ConfigService.ui.cameras.find((camera) => camera.name === cameraName); if (!camera) { diff --git a/src/controller/camera/services/media.service.js b/src/controller/camera/services/media.service.js index 4eebcb10..1532f69f 100644 --- a/src/controller/camera/services/media.service.js +++ b/src/controller/camera/services/media.service.js @@ -22,6 +22,9 @@ export default class MediaService { timedout: false, audio: [], video: [], + bitrate: '? kb/s', + mapvideo: '', + mapaudio: '', }; } @@ -67,16 +70,24 @@ export default class MediaService { ConfigService.ffmpegVersion = line.split(' ')[2]; } + const bitrateLine = line.includes('start: ') && line.includes('bitrate: ') ? line.split('bitrate: ')[1] : false; + + if (bitrateLine) { + this.codecs.bitrate = bitrateLine; + } + const audioLine = line.includes('Audio: ') ? line.split('Audio: ')[1] : false; if (audioLine) { this.codecs.audio = audioLine.split(', '); + this.codecs.mapaudio = line.split('Stream #')[1]?.split(': Audio')[0]; } const videoLine = line.includes('Video: ') ? line.split('Video: ')[1] : false; if (videoLine) { this.codecs.video = videoLine.split(', '); + this.codecs.mapvideo = line.split('Stream #')[1]?.split(': Video')[0]; } lines++; diff --git a/src/main.js b/src/main.js index e5431d63..d477ecd7 100644 --- a/src/main.js +++ b/src/main.js @@ -71,6 +71,10 @@ export default class Interface extends EventEmitter { await controller[1].media.probe(); + if (controller[1].options?.disable) { + return; + } + if (controller[1].options.prebuffering) { await controller[1].prebuffer.start(); } @@ -79,7 +83,7 @@ export default class Interface extends EventEmitter { await controller[1].videoanalysis.start(); } - await controller[1].stream.configureStreamOptions(); + //await controller[1].stream.configureStreamOptions(); }) );