UNPKG

homebridge-eufy-security-mikebcbc

Version:
196 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RecordingDelegate = void 0; const eufy_security_client_1 = require("eufy-security-client"); const ffmpeg_record_1 = require("../utils/ffmpeg-record"); const ffmpeg_params_1 = require("../utils/ffmpeg-params"); const utils_1 = require("../utils/utils"); const MAX_RECORDING_MINUTES = 3; const HKSVQuitReason = [ 'Normal', 'Not allowed', 'Busy', 'Cancelled', 'Unsupported', 'Unexpected Failure', 'Timeout', 'Bad data', 'Protocol error', 'Invalid Configuration', ]; class RecordingDelegate { constructor(streamingDelegate, accessory) { this.streamingDelegate = streamingDelegate; this.accessory = accessory; this.handlingStreamingRequest = false; this.platform = this.streamingDelegate.platform; this.camera = this.streamingDelegate.device; this.cameraName = this.streamingDelegate.cameraName; this.cameraConfig = this.streamingDelegate.cameraConfig; this.localLivestreamManager = this.streamingDelegate.getLivestreamManager(); this.log = this.platform.log; } setController(controller) { this.controller = controller; } isRecording() { return this.handlingStreamingRequest; } async *handleRecordingStreamRequest(streamId) { var _a, _b, _c, _d, _e; this.handlingStreamingRequest = true; this.log.info(`${this.cameraName} requesting recording for HomeKit Secure Video. ID: ${streamId}`); try { // eslint-disable-next-line max-len const audioEnabled = (_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.recordingManagement) === null || _b === void 0 ? void 0 : _b.recordingManagementService.getCharacteristic(this.platform.Characteristic.RecordingAudioActive).value; if (audioEnabled) { this.log.debug('HKSV and plugin are set to record audio.'); } else { this.log.debug('HKSV and plugin are set to omit audio recording.'); } const videoParams = await ffmpeg_params_1.FFmpegParameters.create({ type: 'videoRecording', debug: true }); const audioParams = await ffmpeg_params_1.FFmpegParameters.create({ type: 'audioRecording', debug: true }); videoParams.setupForRecording(this.cameraConfig.videoConfig || {}, this.configuration); audioParams.setupForRecording(this.cameraConfig.videoConfig || {}, this.configuration); const rtsp = (0, utils_1.is_rtsp_ready)(this.camera, this.cameraConfig, this.log); let streamData = null; if (rtsp) { const url = this.camera.getPropertyValue(eufy_security_client_1.PropertyName.DeviceRTSPStreamUrl); this.platform.log.debug(this.cameraName, 'RTSP URL: ' + url); videoParams.setInputSource(url); audioParams === null || audioParams === void 0 ? void 0 : audioParams.setInputSource(url); } else { const value = await this.localLivestreamManager.getLocalLivestream() .catch((err) => { throw (this.cameraName + ' Unable to start the livestream: ' + err); }); streamData = value; videoParams.setInputSource('pipe:3'); audioParams === null || audioParams === void 0 ? void 0 : audioParams.setInputSource('pipe:4'); } this.log.debug(this.cameraName, 'FFMPEG Process definition!'); this.session = new ffmpeg_record_1.FFmpegRecord(`[${this.cameraName}] [HSV Recording Process]`, audioEnabled ? [videoParams, audioParams] : [videoParams], this.platform.ffmpegLogger); this.log.debug(this.cameraName, 'startFragmentedMP4Session Start!'); await this.session.start(); this.log.debug(this.cameraName, 'startFragmentedMP4Session Finish!'); if (this.session.process && this.session.process.stdio) { // stdio is defined and can be used this.log.debug(this.cameraName, 'stdio is defined and can be used!'); if (streamData !== null) { this.log.debug(this.cameraName, 'Stream Data!'); streamData.videostream.pipe(this.session.process.stdio[3]); if (audioEnabled) { streamData.audiostream.pipe(this.session.process.stdio[4]); } } } let timer = MAX_RECORDING_MINUTES * 60; if (this.platform.config.CameraMaxLivestreamDuration < timer) { timer = this.platform.config.CameraMaxLivestreamDuration; } if (timer > 0) { this.forceStopTimeout = setTimeout(() => { var _a; this.log.warn(this.cameraName, `The recording process has been running for ${timer} seconds and is now being forced closed!`); (_a = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _a === void 0 ? void 0 : _a.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false); }, timer * 1000); } let isMotionActive = true; setTimeout(() => { isMotionActive = false; this.log.debug(this.cameraName, 'Ending recording session due to motion stopped!'); }, 60 * 1000); // Set the flag to false after 15 seconds this.log.debug(this.cameraName, 'Generator Start!'); for await (const segment of this.session.segmentGenerator()) { if (!this.isRecording()) { this.log.debug(this.cameraName, 'Recording was ended prematurely.'); break; } if (!segment) { continue; } this.log.debug(`${this.cameraName} Yeah a good segment!`); const motionDetected = (_c = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _c === void 0 ? void 0 : _c.getCharacteristic(this.platform.Characteristic.MotionDetected).value; yield { data: segment, isLast: !isMotionActive, }; if (!isMotionActive) { this.log.debug(this.cameraName, 'Ending recording session due to motion stopped!'); break; } } } catch (error) { if (!this.handlingStreamingRequest && this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { this.log.debug(this.cameraName, 'Recording encountered an error but that is expected, as the recording was canceled beforehand. Error: ' + error); } else { this.log.error(this.cameraName, 'Error while recording: ' + error); } } finally { if (this.closeReason && this.closeReason !== 0 /* HDSProtocolSpecificErrorReason.NORMAL */ && this.closeReason !== 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { this.log.warn(this.cameraName, `The recording process was aborted by HSV with reason "${HKSVQuitReason[this.closeReason]}"`); } if (this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) { this.log.debug(this.cameraName, 'The recording process was canceled by the HomeKit Controller."'); } if (this.forceStopTimeout) { clearTimeout(this.forceStopTimeout); this.forceStopTimeout = undefined; } // check whether motion is still in progress const motionDetected = (_d = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _d === void 0 ? void 0 : _d.getCharacteristic(this.platform.Characteristic.MotionDetected).value; if (motionDetected) { (_e = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _e === void 0 ? void 0 : _e.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false); } this.log.debug(this.cameraName, 'Streaming STOP! Recording!'); this.localLivestreamManager.stopLocalLiveStream(); } } updateRecordingActive(active) { this.log.debug(`Recording: ${active}`, this.accessory.displayName); } updateRecordingConfiguration(configuration) { this.configuration = configuration; } closeRecordingStream(streamId, reason) { var _a, _b, _c; this.log.info(this.cameraName, 'Closing recording process'); if (this.session) { this.log.debug(this.cameraName, 'Stopping recording session.'); (_a = this.session.process) === null || _a === void 0 ? void 0 : _a.kill('SIGKILL'); this.session = undefined; } else { this.log.warn('Recording session could not be closed gracefully.'); } if (this.forceStopTimeout) { clearTimeout(this.forceStopTimeout); this.forceStopTimeout = undefined; } // check whether motion is still in progress const motionDetected = (_b = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _b === void 0 ? void 0 : _b.getCharacteristic(this.platform.Characteristic.MotionDetected).value; if (motionDetected) { (_c = this.accessory .getService(this.platform.Service.MotionSensor)) === null || _c === void 0 ? void 0 : _c.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false); } this.closeReason = reason; this.handlingStreamingRequest = false; } acknowledgeStream(streamId) { this.log.debug('end of recording acknowledged!'); this.closeRecordingStream(streamId, undefined); } } exports.RecordingDelegate = RecordingDelegate; //# sourceMappingURL=recordingDelegate.js.map