Allow Recording Overlap with Event-Based Recording (optional)

master
Moe 2024-10-21 17:31:06 -07:00
parent 9a8fb32f7e
commit e064dcde54
4 changed files with 84 additions and 25 deletions

View File

@ -3425,6 +3425,24 @@ module.exports = function(s,config,lang){
field: lang['Probe Size'],
default: '32',
},
{
"name": "detail=detector_record_overlap",
"field": lang['Recording Overlap'],
"description": lang.fieldTextRecordingOverlap,
"default": "0",
"form-group-class-pre-layer": "h_rec_mtd_input h_rec_mtd_hot h_rec_mtd_sip",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"fieldType": "div",
// style: `width:100%;background:#eceaea;border-radius:5px;color:#333;font-family:monospace`,

View File

@ -931,6 +931,7 @@
"Account Settings": "Account Settings",
"How to Record": "How to Record",
"Trigger Record": "Trigger Record",
"Recording Overlap": "Recording Overlap",
"Recording Timeout": "Recording Timeout <small>in Minutes</small>",
"Timeout Reset on Next Motion": "Timeout Reset on Next Motion",
"Timeout Reset on Next Event": "Timeout Reset on Next Event",
@ -1608,6 +1609,7 @@
"MQTT Client": "MQTT Client",
"Buffer Time from Event": "Buffer Time from Event",
"detected": "detected",
"fieldTextRecordingOverlap": "Allow multiple Event-Based Recordings to run in parallel. Only use this if you need to have a new recording started after each detector trigger.",
"fieldTextDetectorsSelected": "Select which detectors to send frames to.",
"fieldTextEventFilters": "Enable to have all Events honor your Event Filter rules.",
"fieldTextBufferTimeFromEvent": "The amount of seconds to record before the trigger happened. If this is consistently inaccurate you will need to look at the <a target='_blank' href='https://hub.shinobi.video/articles/view/DmWIID78VtvEfnf'>optimization guide</a> or force encoding on the server.",

View File

@ -533,32 +533,35 @@ module.exports = (s,config,lang) => {
const activeMonitor = s.group[groupKey].activeMonitors[monitorId]
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const monitorDetails = monitorConfig.details
const overlappingRecordings = monitorDetails.detector_record_overlap === '1'
if(monitorDetails.detector !== '1'){
return
}
var detector_timeout
if(!overlappingRecordings && activeMonitor.eventBasedRecordingLastFileTime)fileTime = activeMonitor.eventBasedRecordingLastFileTime;
var detector_timeout;
if(!monitorDetails.detector_timeout||monitorDetails.detector_timeout===''){
detector_timeout = 10
}else{
detector_timeout = parseFloat(monitorDetails.detector_timeout)
}
if(monitorDetails.watchdog_reset === '1' || !activeMonitor.eventBasedRecording.timeout){
clearTimeout(activeMonitor.eventBasedRecording.timeout)
activeMonitor.eventBasedRecording.timeout = setTimeout(function(){
activeMonitor.eventBasedRecording.allowEnd = true
if(!activeMonitor.eventBasedRecording[fileTime])activeMonitor.eventBasedRecording[fileTime] = {};
if((!overlappingRecordings && monitorDetails.watchdog_reset === '1') || !activeMonitor.eventBasedRecording[fileTime].timeout){
clearTimeout(activeMonitor.eventBasedRecording[fileTime].timeout)
activeMonitor.eventBasedRecording[fileTime].timeout = setTimeout(function(){
activeMonitor.eventBasedRecording[fileTime].allowEnd = true
try{
activeMonitor.eventBasedRecording.process.stdin.setEncoding('utf8')
activeMonitor.eventBasedRecording.process.stdin.write('q')
activeMonitor.eventBasedRecording[fileTime].process.stdin.setEncoding('utf8')
activeMonitor.eventBasedRecording[fileTime].process.stdin.write('q')
}catch(err){
s.debugLog(err)
}
activeMonitor.eventBasedRecording.process.kill('SIGINT')
delete(activeMonitor.eventBasedRecording.timeout)
activeMonitor.eventBasedRecording[fileTime].process.kill('SIGINT')
delete(activeMonitor.eventBasedRecording[fileTime].timeout)
},detector_timeout * 1000 * 60)
}
if(!activeMonitor.eventBasedRecording.process){
activeMonitor.eventBasedRecording.allowEnd = false;
activeMonitor.eventBasedRecording.lastFileTime = `${fileTime}`;
if(!activeMonitor.eventBasedRecording[fileTime].process){
activeMonitor.eventBasedRecording[fileTime].allowEnd = false;
activeMonitor.eventBasedRecordingLastFileTime = `${fileTime}`;
const runRecord = function(){
var ffmpegError = ''
var error
@ -586,18 +589,18 @@ module.exports = (s,config,lang) => {
let LiveStartIndex = parseInt(secondsBefore / 2 + 1)
const ffmpegCommand = `-loglevel warning -live_start_index -${LiveStartIndex} -analyzeduration ${analyzeDuration} -probesize ${probeSize} -re -i "${s.dir.streams+groupKey+'/'+monitorId}/detectorStream.m3u8" ${outputMap}-movflags faststart -fflags +igndts -c:v copy -c:a aac -strict -2 -strftime 1 -y "${s.getVideoDirectory(monitorConfig) + filename}"`
s.debugLog(ffmpegCommand)
activeMonitor.eventBasedRecording.process = spawn(
activeMonitor.eventBasedRecording[fileTime].process = spawn(
config.ffmpegDir,
splitForFFMPEG(ffmpegCommand)
)
activeMonitor.eventBasedRecording.process.stderr.on('data',function(data){
activeMonitor.eventBasedRecording[fileTime].process.stderr.on('data',function(data){
s.userLog(d,{
type: logTitleText,
msg: data.toString()
})
})
activeMonitor.eventBasedRecording.process.on('close',function(){
if(!activeMonitor.eventBasedRecording.allowEnd){
activeMonitor.eventBasedRecording[fileTime].process.on('close',function(){
if(!activeMonitor.eventBasedRecording[fileTime].allowEnd){
s.userLog(d,{
type: logTitleText,
msg: lang["Detector Recording Process Exited Prematurely. Restarting."]
@ -627,15 +630,15 @@ module.exports = (s,config,lang) => {
s.userLog(d,{
type: logTitleText,
msg: lang["Detector Recording Complete"]
})
});
s.userLog(d,{
type: logTitleText,
msg: lang["Clear Recorder Process"]
})
delete(activeMonitor.eventBasedRecording.process)
clearTimeout(activeMonitor.eventBasedRecording.timeout)
delete(activeMonitor.eventBasedRecording.timeout)
});
if(!overlappingRecordings)delete(activeMonitor.eventBasedRecordingLastFileTime)
clearTimeout(activeMonitor.eventBasedRecording[fileTime].timeout)
clearTimeout(activeMonitor.recordingChecker)
delete(activeMonitor.eventBasedRecording[fileTime])
})
}
runRecord()
@ -643,10 +646,13 @@ module.exports = (s,config,lang) => {
}
const closeEventBasedRecording = function(e){
const activeMonitor = s.group[e.ke].activeMonitors[e.id]
if(activeMonitor.eventBasedRecording.process){
clearTimeout(activeMonitor.eventBasedRecording.timeout)
activeMonitor.eventBasedRecording.allowEnd = true
activeMonitor.eventBasedRecording.process.kill('SIGTERM')
const eventBasedRecordings = activeMonitor.eventBasedRecording;
for(fileTime in eventBasedRecordings){
if(eventBasedRecordings[fileTime].process){
clearTimeout(eventBasedRecordings[fileTime].timeout)
eventBasedRecordings[fileTime].allowEnd = true
eventBasedRecordings[fileTime].process.kill('SIGTERM')
}
}
// var stackedProcesses = s.group[e.ke].activeMonitors[e.id].eventBasedRecording.stackable
// Object.keys(stackedProcesses).forEach(function(key){

View File

@ -957,6 +957,39 @@ module.exports = function(s,config,lang,app,io){
s.closeJsonResponse(res,response);
},res,req);
});
/**
* API : Get Active Event-Based Recording Info
*/
app.get(config.webPaths.apiPrefix+':auth/eventRecordings/:ke/:id', function (req,res){
const response = {ok: false};
s.auth(req.params,async (user) => {
const groupKey = req.params.ke
const monitorId = req.params.id
const {
monitorPermissions,
monitorRestrictions,
} = s.getMonitorsPermitted(user.details,monitorId)
const {
isRestricted,
userPermissions,
apiKeyPermissions,
isRestrictedApiKey,
} = s.checkPermission(user)
if(
isRestrictedApiKey && apiKeyPermissions.control_monitors_disallowed ||
isRestricted && !monitorPermissions[`${monitorId}_monitors`]
){
response.msg = user.lang['Not Permitted']
}else{
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const activeMonitor = s.group[groupKey].activeMonitors[monitorId]
const fileTimes = Object.keys(activeMonitor.eventBasedRecording)
response.fileTimes = fileTimes;
response.ok = true;
}
s.closeJsonResponse(res,response);
},res,req);
});
// /**
// * API : Merge Recorded Videos into one file
// */