@camera.ui/server 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/interface/assets/{404-pb2fRn1r.js → 404-6v3-U7nj.js} +1 -1
- package/dist/interface/assets/{About-eljMnDub.js → About-IMZSnCy7.js} +1 -1
- package/dist/interface/assets/{Adminpanel-BtQdiyre.js → Adminpanel-jq9sK1Ey.js} +1 -1
- package/dist/interface/assets/{Automation-DOiLJcU_.js → Automation-xs_GEpGx.js} +3 -3
- package/dist/interface/assets/{AutomationImport-DflZNRBB.js → AutomationImport-CR4fWWkU.js} +2 -2
- package/dist/interface/assets/{AutomationStore-qF_NTNUf.js → AutomationStore-rB-eVXdY.js} +2 -2
- package/dist/interface/assets/{AutomationStoreDetail-CUw-jBBR.js → AutomationStoreDetail-DzMg8IOO.js} +2 -2
- package/dist/interface/assets/Automations-DWI8Q2TS.js +2 -0
- package/dist/interface/assets/Camera-BIppnvyN.js +2 -0
- package/dist/interface/assets/Camera-BfMZmurS.css +1 -0
- package/dist/interface/assets/{CameraConsole-CEARcJYc.js → CameraConsole-CQLrlDvb.js} +2 -2
- package/dist/interface/assets/{Cameras-C0oHuoTR.js → Cameras-kIN6bWi1.js} +2 -2
- package/dist/interface/assets/Camview-4jdGGny8.js +1 -0
- package/dist/interface/assets/Camview-UCj_OW0s.css +1 -0
- package/dist/interface/assets/Config-DxYeS7cY.js +2 -0
- package/dist/interface/assets/{ConfigActionCameraControl-De0qb88r.js → ConfigActionCameraControl-Biu5BwNu.js} +1 -1
- package/dist/interface/assets/{ConfigActionDelay-nDK4i-AL.js → ConfigActionDelay-CK5j4XLw.js} +1 -1
- package/dist/interface/assets/{ConfigActionHttp-DX-vapgv.js → ConfigActionHttp-Dtvi3jZB.js} +1 -1
- package/dist/interface/assets/{ConfigActionImageInput-CS7U7BsI.js → ConfigActionImageInput-Dh0A20j9.js} +1 -1
- package/dist/interface/assets/{ConfigActionMqtt-82swCx_F.js → ConfigActionMqtt-DwZdnDlb.js} +1 -1
- package/dist/interface/assets/{ConfigActionNotification-t-1zN_1y.js → ConfigActionNotification-BVqYxGu8.js} +1 -1
- package/dist/interface/assets/{ConfigActionNotificationControl-jz3ez2Uq.js → ConfigActionNotificationControl-BTdnQqox.js} +1 -1
- package/dist/interface/assets/{ConfigActionOutput-B6uJheX1.js → ConfigActionOutput-Dv745ppL.js} +1 -1
- package/dist/interface/assets/{ConfigActionPlugin-LyOIrZnv.js → ConfigActionPlugin-C9QdPDXC.js} +1 -1
- package/dist/interface/assets/{ConfigActionSensor-BTqc1inw.js → ConfigActionSensor-D17wASao.js} +1 -1
- package/dist/interface/assets/{ConfigActionSnapshot-DdPy_Ubu.js → ConfigActionSnapshot-CrbMLwuh.js} +1 -1
- package/dist/interface/assets/{ConfigActionVariable-Csf7jPOm.js → ConfigActionVariable-C0HobSMN.js} +1 -1
- package/dist/interface/assets/{ConfigConditionIfElse-BvzwZ70p.js → ConfigConditionIfElse-DsedkGhY.js} +1 -1
- package/dist/interface/assets/ConfigConditionSensorState-rRLbA2_b.js +1 -0
- package/dist/interface/assets/{ConfigConditionSwitch-DJU2JC-3.js → ConfigConditionSwitch-5nEVS-Oj.js} +1 -1
- package/dist/interface/assets/{ConfigConditionTime-BxE2CJMA.js → ConfigConditionTime-CieLn-sp.js} +1 -1
- package/dist/interface/assets/ConfigSensorValueInput-CRRM_aBz.js +1 -0
- package/dist/interface/assets/{ConfigTriggerDetection-DpC8V8K8.js → ConfigTriggerDetection-CMVgejZ3.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerGeofence-DTzax7bc.js → ConfigTriggerGeofence-cMhyndiz.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerManual-CtjnnQb1.js → ConfigTriggerManual-Bd9H0AfP.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerMqtt-C7J22EMe.js → ConfigTriggerMqtt-BIv5Ca91.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerSchedule-DnYDDz3F.js → ConfigTriggerSchedule-CzWWcnFW.js} +1 -1
- package/dist/interface/assets/ConfigTriggerSensor-C5lsyxsh.js +1 -0
- package/dist/interface/assets/{ConfigTriggerSystem-D1JulYVd.js → ConfigTriggerSystem-WipCRwTJ.js} +1 -1
- package/dist/interface/assets/{ConfigTriggerWebhook-BrKFtq7o.js → ConfigTriggerWebhook-Bl0O4RBX.js} +1 -1
- package/dist/interface/assets/Console-GtK0GVLX.js +1 -0
- package/dist/interface/assets/CuiAutomationRunsDialog-CUPBuq_J.js +1 -0
- package/dist/interface/assets/CuiBottomSheet-DQNpk0mh.js +1 -0
- package/dist/interface/assets/{CuiBottomSheet-C_1_moNo.js → CuiBottomSheet-K19nZQiG.js} +1 -1
- package/dist/interface/assets/CuiCameraPipCard-e3_uYELi.js +1 -0
- package/dist/interface/assets/CuiCameraRecordings-CPAMltfx.js +1 -0
- package/dist/interface/assets/CuiCameraRecordings-fB3X6kad.css +1 -0
- package/dist/interface/assets/{CuiCameraShares-XwN5kuC7.js → CuiCameraShares-BTn5ys6X.js} +1 -1
- package/dist/interface/assets/{CuiCameraTable-BeeHLaiQ.js → CuiCameraTable-DYvQ-GCz.js} +2 -2
- package/dist/interface/assets/{CuiConsole-B7R7U9UZ.js → CuiConsole-C4PawCou.js} +2 -2
- package/dist/interface/assets/{CuiDataTable-CQhCu0px.js → CuiDataTable-CORQOWsf.js} +1 -1
- package/dist/interface/assets/CuiLightControl-BYkQ8Ue3.js +1 -0
- package/dist/interface/assets/CuiList-BgZBIhXP.js +1 -0
- package/dist/interface/assets/CuiMarkdownContent-CCdOW_TM.js +1 -0
- package/dist/interface/assets/CuiMarkdownContent-ChezqgI1.js +1 -0
- package/dist/interface/assets/{CuiPluginCard-rkhmuumi.css → CuiPluginCard-C3ANAXSI.css} +1 -1
- package/dist/interface/assets/CuiPluginCard-C91rKOge.js +2 -0
- package/dist/interface/assets/{CuiPluginCategoryChip-JkdbQU4X.js → CuiPluginCategoryChip-B_Zy4B42.js} +1 -1
- package/dist/interface/assets/{CuiPluginOAuthButton-LYMFfb3v.js → CuiPluginOAuthButton-IXGedGWs.js} +2 -2
- package/dist/interface/assets/{CuiPluginTrustBadge-sZF0UKMD.js → CuiPluginTrustBadge-DTfM8Jjo.js} +1 -1
- package/dist/interface/assets/{CuiQRCode-B4ysluWR.js → CuiQRCode-D5vwhyuQ.js} +1 -1
- package/dist/interface/assets/CuiRecordingsGrid-Sv3uyj9a.css +1 -0
- package/dist/interface/assets/CuiRecordingsGrid-yXEzhnc4.js +1 -0
- package/dist/interface/assets/CuiSchema-hK_tgUP9.js +1 -0
- package/dist/interface/assets/{CuiTopNavbarItem-rMsWME9B.js → CuiTopNavbarItem-DWLnn7Kh.js} +1 -1
- package/dist/interface/assets/Faces-DzwTi8P1.js +1 -0
- package/dist/interface/assets/FirstSteps-0ojmFGSr.js +1 -0
- package/dist/interface/assets/Instances-DvoFBGMx.js +1 -0
- package/dist/interface/assets/{Logs-8onUmCYX.js → Logs-Cii4oP8R.js} +2 -2
- package/dist/interface/assets/Menu-pvtuFKFM.js +1 -0
- package/dist/interface/assets/OAuthDeviceFlow-BZgtsIjF.js +1 -0
- package/dist/interface/assets/Plugin-BDqNeTCT.css +1 -0
- package/dist/interface/assets/Plugin-BKqpBWJy.js +1 -0
- package/dist/interface/assets/{PluginConsole-JFcH_ZW0.js → PluginConsole-BFsCogtB.js} +1 -1
- package/dist/interface/assets/PluginDetail-Dl0OM6me.js +2 -0
- package/dist/interface/assets/{PluginSchema-Gfn_csMP.js → PluginSchema-D45zblVz.js} +1 -1
- package/dist/interface/assets/PluginSearch-DdIcv9xA.js +1 -0
- package/dist/interface/assets/{PluginSearch-CLjLT9JT.css → PluginSearch-Dlzc-aJ3.css} +1 -1
- package/dist/interface/assets/PluginSearch-DxoQUW8M.js +2 -0
- package/dist/interface/assets/{PluginUpdateProgress-DaI9bwSV.js → PluginUpdateProgress-CsRGGH8J.js} +1 -1
- package/dist/interface/assets/Plugins-CGj8DSLJ.js +1 -0
- package/dist/interface/assets/Recordings-PojBH8L3.js +1 -0
- package/dist/interface/assets/SensorEdit-D2ChsNDL.js +1 -0
- package/dist/interface/assets/{SensorHistory-2TmYLaL4.js → SensorHistory-B6ZKgot4.js} +1 -1
- package/dist/interface/assets/Sensors-B-Qhfr5i.css +1 -0
- package/dist/interface/assets/Sensors-DyqWcB-G.js +2 -0
- package/dist/interface/assets/{Settings-8yG5ny5r.js → Settings-BFJO08mq.js} +1 -1
- package/dist/interface/assets/SettingsAccount-DREZwvce.js +1 -0
- package/dist/interface/assets/SettingsAccount-QxmP0dzI.js +1 -0
- package/dist/interface/assets/SettingsAppearance-BejKI3_C.js +1 -0
- package/dist/interface/assets/{SettingsAppearance-WQtOksbk.js → SettingsAppearance-_zvMUQvn.js} +1 -1
- package/dist/interface/assets/SettingsBackup-8HCwVmDA.js +1 -0
- package/dist/interface/assets/SettingsList-B4H786Cc.js +1 -0
- package/dist/interface/assets/SettingsMqtt-D7ajm1JA.js +1 -0
- package/dist/interface/assets/{SettingsNotifications-BVGmt0eF.js → SettingsNotifications-fDDuqryh.js} +2 -2
- package/dist/interface/assets/{SettingsPermissions-BPqDIjVt.js → SettingsPermissions-DYirYf1Z.js} +1 -1
- package/dist/interface/assets/SettingsRecordings-4NxpAbu5.js +1 -0
- package/dist/interface/assets/SettingsRemote-DOQkaNzp.js +1 -0
- package/dist/interface/assets/SettingsSystem-CVWkw449.js +2 -0
- package/dist/interface/assets/{SettingsUsers-BtT17FNe.js → SettingsUsers-Dc6L7Lt2.js} +1 -1
- package/dist/interface/assets/Terminal-BgVnN-2g.js +4 -0
- package/dist/interface/assets/{VariableInput-CkXZ00Gc.js → VariableInput-ZYn-Phl4.js} +1 -1
- package/dist/interface/assets/{VariableSuggestions-BD1kOG5f.js → VariableSuggestions-BnzdQP6R.js} +1 -1
- package/dist/interface/assets/VersionsHandler-BvKvGOlF.js +7 -0
- package/dist/interface/assets/{VirtualSensorCreate-DcwJ_w-l.js → VirtualSensorCreate-Dk_YYzxR.js} +1 -1
- package/dist/interface/assets/Workers-CjEzGjy8.js +2 -0
- package/dist/interface/assets/{account-CY80SLLf.js → account-DuSFtIHf.js} +1 -1
- package/dist/interface/assets/{alert-circle-pDGfnZ4K.js → alert-circle-C2AU59aR.js} +1 -1
- package/dist/interface/assets/{alert-outline-CeoXcnQc.js → alert-outline-GrD7-zHq.js} +1 -1
- package/dist/interface/assets/{api-Mny5Elfn.js → api-36pS71R2.js} +1 -1
- package/dist/interface/assets/api-CDvtqu1L.css +2 -0
- package/dist/interface/assets/api-DTFsk1zB.js +2 -0
- package/dist/interface/assets/{arrow-left-D9jDhvK5.js → arrow-left-C1N6U6kZ.js} +1 -1
- package/dist/interface/assets/{arrow-right-outline-DF7tkKYk.js → arrow-right-outline-C24f5mwe.js} +1 -1
- package/dist/interface/assets/{auth-9_Jjlsh0.js → auth-DYoO8jpd.js} +1 -1
- package/dist/interface/assets/automations-BOvDlvvx.js +1 -0
- package/dist/interface/assets/{back-filled-DUUGErMw.js → back-filled-DcBDBvvP.js} +1 -1
- package/dist/interface/assets/{bell-ring-DV73JIre.js → bell-ring-DSX81yd4.js} +1 -1
- package/dist/interface/assets/cameras.schema-BvnUX82v.js +1 -0
- package/dist/interface/assets/{cctv-jy5ROx0o.js → cctv-BA5DvNlE.js} +1 -1
- package/dist/interface/assets/{check-circle-FwoylV5-.js → check-circle-Do_mBN_A.js} +1 -1
- package/dist/interface/assets/{chevron-down-umZ7i0mG.js → chevron-down-D111daTn.js} +1 -1
- package/dist/interface/assets/{clock-outline-CbvOAMhO.js → clock-outline-DF1wLNzs.js} +1 -1
- package/dist/interface/assets/{close-CtaDF28n.js → close-BNAWPz0y.js} +1 -1
- package/dist/interface/assets/{close-circle-FZ8IxGd4.js → close-circle-zhDWvz98.js} +1 -1
- package/dist/interface/assets/{cloud-check-CFSukfAF.js → cloud-check-pUK1Fpt9.js} +1 -1
- package/dist/interface/assets/cloudHandoff-DNKMEHOI.js +1 -0
- package/dist/interface/assets/{config-BZphegxi.js → config-DTGhJmsj.js} +1 -1
- package/dist/interface/assets/{copy-CFA_pDRv.js → copy-BPT84cDt.js} +1 -1
- package/dist/interface/assets/{cube-scan-DEuMi23T.js → cube-scan-DOe6W9mW.js} +1 -1
- package/dist/interface/assets/{delete-921w_nKy.js → delete-BF6keuqy.js} +1 -1
- package/dist/interface/assets/{deployed-code-update-CYS6eKfa.js → deployed-code-update-xHE-blDN.js} +1 -1
- package/dist/interface/assets/{dist-BwEXwXSw.js → dist-Yc5s0_JB.js} +1 -1
- package/dist/interface/assets/{download-C-LzNRmM.js → download-BCPfm6aN.js} +1 -1
- package/dist/interface/assets/esm-B3yVlCMw.js +1 -0
- package/dist/interface/assets/esm-BKQ7CQK7.js +1 -0
- package/dist/interface/assets/esm-BiSE05P9.js +1 -0
- package/dist/interface/assets/esm-BoPthGAl.js +1 -0
- package/dist/interface/assets/esm-C2ALrdyx.js +1 -0
- package/dist/interface/assets/esm-CKEGzys4.js +1 -0
- package/dist/interface/assets/esm-CctiCHtK.js +1 -0
- package/dist/interface/assets/esm-Cqq81lnk.js +1 -0
- package/dist/interface/assets/esm-CyrLQTu0.js +1 -0
- package/dist/interface/assets/esm-D6O-eZYo.js +1 -0
- package/dist/interface/assets/esm-DG1f0qwo.js +1 -0
- package/dist/interface/assets/esm-DGegIZMF.js +1 -0
- package/dist/interface/assets/esm-JGwpHVqo.js +1 -0
- package/dist/interface/assets/esm-TRigYTsA.js +1 -0
- package/dist/interface/assets/{eye-off-DESV3QdF.js → eye-off-CaRq7Ks2.js} +1 -1
- package/dist/interface/assets/{flowSchema-_RiPAgMv.js → flowSchema-zo1H4tnP.js} +1 -1
- package/dist/interface/assets/{history-60iCpGvQ.js → history-SuH8IHAj.js} +1 -1
- package/dist/interface/assets/{home-w5iY2RUg.js → home-CUykMSBW.js} +1 -1
- package/dist/interface/assets/{information-outline-CvBsGtDu.js → information-outline-BQDq8w2t.js} +1 -1
- package/dist/interface/assets/{lightbulb-outline-DLrnCPG5.js → lightbulb-outline-ClK17CO6.js} +1 -1
- package/dist/interface/assets/main-CcvCf5qj.js +3 -0
- package/dist/interface/assets/{main-DJb49Tjp.css → main-XQ5Vahbs.css} +1 -1
- package/dist/interface/assets/{microphone-DUVI-s2_.js → microphone-C0weJY9W.js} +1 -1
- package/dist/interface/assets/minus-RI3VlDe8.js +1 -0
- package/dist/interface/assets/mqtt-CYc8_9pb.js +1 -0
- package/dist/interface/assets/native-B01iGjCh.js +1 -0
- package/dist/interface/assets/native-D381pxlG.js +1 -0
- package/dist/interface/assets/{nodeDefinitions-BecZ8I2Q.js → nodeDefinitions-DQQlFmPN.js} +1 -1
- package/dist/interface/assets/{notifications-CK6XQBF-.js → notifications-cHS8-1HN.js} +1 -1
- package/dist/interface/assets/{pencil-outline-BrOrbuzc.js → pencil-outline-D7aD4cCo.js} +1 -1
- package/dist/interface/assets/plugin-W20ihuog.js +1 -0
- package/dist/interface/assets/plugins-CaKErv4E.js +1 -0
- package/dist/interface/assets/{plus-Dd-XLT76.js → plus-CA5o30dE.js} +1 -1
- package/dist/interface/assets/{plus-CL6KSxf8.js → plus-DHwFyXkb.js} +1 -1
- package/dist/interface/assets/{power-lCbm_7y7.js → power-C9UIp2oO.js} +1 -1
- package/dist/interface/assets/{ring-resize-CEqUkpxT.js → ring-resize-DkY5EgVl.js} +1 -1
- package/dist/interface/assets/{round-alt-arrow-left-bold-CwaLYdW5.js → round-alt-arrow-left-bold-BxX4ufd0.js} +1 -1
- package/dist/interface/assets/{round-restart-alt-BFgY3k77.js → round-restart-alt-DPYbDt-x.js} +1 -1
- package/dist/interface/assets/{search-CT2BLw7G.js → search-BZkSqZO5.js} +1 -1
- package/dist/interface/assets/sensorIcons-DepqfSoA.js +1 -0
- package/dist/interface/assets/sensors-CMpbwMO4.js +1 -0
- package/dist/interface/assets/{server-BSYs0zoo.js → server-C-WV93FX.js} +1 -1
- package/dist/interface/assets/{settings-CzS4dvIO.js → settings-BUEZ5iRE.js} +1 -1
- package/dist/interface/assets/{source-branch-C6p1G3a9.js → source-branch-ztDB0e13.js} +1 -1
- package/dist/interface/assets/{table-2-line-_VC4NuFA.js → table-2-line-B-z0xvbG.js} +1 -1
- package/dist/interface/assets/{theme-Dr_YO8-0.js → theme-BkwBz73p.js} +1 -1
- package/dist/interface/assets/{trash-2-DcOYT3oH.js → trash-2-DMMlSPXn.js} +1 -1
- package/dist/interface/assets/{trash-can-outline-cphfB8Jh.js → trash-can-outline-DLKniIWM.js} +1 -1
- package/dist/interface/assets/{trash-fill-CRBaUdPn.js → trash-fill-DevIK050.js} +1 -1
- package/dist/interface/assets/{trash2-CvhB8xOW.js → trash2-BLScx4HX.js} +1 -1
- package/dist/interface/assets/{types-3mdj4rRK.js → types-Cko7VOxd.js} +1 -1
- package/dist/interface/assets/types-ClTvDPjn.js +1 -0
- package/dist/interface/assets/{types-YwmhC3Ej.js → types-vaT6wuY0.js} +1 -1
- package/dist/interface/assets/{upload-DeN3O-ua.js → upload-ClkX9wUX.js} +1 -1
- package/dist/interface/assets/{useAppVersion-UIA6seZv.js → useAppVersion-V1S18l98.js} +1 -1
- package/dist/interface/assets/useBackupRestore-BZ73vHrV.js +1 -0
- package/dist/interface/assets/{useCameraOptions-Dx-zyqdH.js → useCameraOptions-o1hYGLbC.js} +1 -1
- package/dist/interface/assets/{useElectron-BXVcamD0.js → useElectron-3ZJLtRk3.js} +1 -1
- package/dist/interface/assets/{useLogsSocket-DSxzAvQF.js → useLogsSocket-DDgnQ_Dk.js} +1 -1
- package/dist/interface/assets/{usePermissions-B_C-zX5H.js → usePermissions-BWlj5SzG.js} +1 -1
- package/dist/interface/assets/{usePluginUpdates-QU3U82S_.js → usePluginUpdates-DI1p2NMc.js} +1 -1
- package/dist/interface/assets/{useServerRestart-BxDBO7k-.js → useServerRestart-D_ASBw_0.js} +1 -1
- package/dist/interface/assets/{useStreamStatus-DY4NM_Ua.js → useStreamStatus-BQJIFFLv.js} +1 -1
- package/dist/interface/assets/useUpdateChannel-CyLSUSAY.js +1 -0
- package/dist/interface/assets/useUserOptions-Ds1Ybn7N.js +1 -0
- package/dist/interface/assets/{variable-oLSsiEtj.js → variable-CCMnQpL2.js} +1 -1
- package/dist/interface/assets/{vendor-JQXDMBA-.js → vendor-BaiDFVEr.js} +17 -16
- package/dist/interface/assets/vendor-cameraui-CQTIVF_m.js +2326 -0
- package/dist/interface/assets/{vendor-capacitor-CDwgXF4b.js → vendor-capacitor-4nn-eF9U.js} +2 -2
- package/dist/interface/assets/{vendor-chart-KrNcA52y.js → vendor-chart-DtwjR6yw.js} +1 -1
- package/dist/interface/assets/vendor-core-DDtZhtY9.js +1 -0
- package/dist/interface/assets/{vendor-dnd-Jch9IvL6.js → vendor-dnd-ChrDrMuz.js} +1 -1
- package/dist/interface/assets/{vendor-draggabilly-Cn-knCpG.js → vendor-draggabilly-DSeIE_bQ.js} +1 -1
- package/dist/interface/assets/{vendor-markdown-BhFLJolW.js → vendor-markdown--U0EK8HV.js} +1 -1
- package/dist/interface/assets/{vendor-primevue-C0acGg0Y.js → vendor-primevue-DE-f4Z5f.js} +76 -76
- package/dist/interface/assets/{vendor-vueflow-DQrTmoNS.js → vendor-vueflow-CLJNLJXc.js} +1 -1
- package/dist/interface/assets/web-BNRwhIOt.js +1 -0
- package/dist/interface/assets/web-BPkOXv4d.js +1 -0
- package/dist/interface/assets/web-BZIx_2d3.js +1 -0
- package/dist/interface/assets/web-BjKL51lK-BwChM7zd.js +1 -0
- package/dist/interface/assets/web-BkmtZjSt.js +1 -0
- package/dist/interface/assets/web-C6RgriWz.js +1 -0
- package/dist/interface/assets/web-CQFMLa0a.js +1 -0
- package/dist/interface/assets/web-CZGnd6VE.js +1 -0
- package/dist/interface/assets/web-Cfzay11X.js +1 -0
- package/dist/interface/assets/web-DAgBwKL-.js +1 -0
- package/dist/interface/assets/web-DgVHh5ek.js +1 -0
- package/dist/interface/assets/web-DlSXwZBR.js +1 -0
- package/dist/interface/assets/web-eDYxVGH7.js +1 -0
- package/dist/interface/assets/web-h4k3EoRQ.js +1 -0
- package/dist/interface/assets/{webhook-CMAB4nTl.js → webhook-DQiZUPv8.js} +1 -1
- package/dist/interface/assets/workbox-window.prod.es5-CcJiPjAS.js +1 -0
- package/dist/interface/assets/{zoom-in-STm8SNt2.js → zoom-in-Du1IB9oA.js} +1 -1
- package/dist/interface/index.html +42 -41
- package/dist/interface/sw.js +1 -1
- package/dist/server/package.json +14 -14
- package/dist/server/requirements.txt +2 -2
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/__init__.py +6 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/__init__.py +0 -2
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/events.py +11 -47
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/frames.py +2 -4
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/internal/sensor_rpc.py +6 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/plugin/contract.py +1 -1
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/plugin/notifier.py +7 -0
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/base.py +18 -1
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/classifier.py +14 -8
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/face.py +23 -7
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/license_plate.py +23 -7
- package/dist/server/src/_camera_ui_tools/camera_ui_sdk/storage/__init__.py +60 -2
- package/dist/server/src/api/controllers/automations.controller.d.ts +3 -1
- package/dist/server/src/api/controllers/automations.controller.js +27 -0
- package/dist/server/src/api/controllers/automations.controller.js.map +1 -1
- package/dist/server/src/api/controllers/cameras.controller.d.ts +3 -2
- package/dist/server/src/api/controllers/cameras.controller.js +26 -1
- package/dist/server/src/api/controllers/cameras.controller.js.map +1 -1
- package/dist/server/src/api/controllers/instances.controller.d.ts +2 -1
- package/dist/server/src/api/controllers/instances.controller.js +9 -0
- package/dist/server/src/api/controllers/instances.controller.js.map +1 -1
- package/dist/server/src/api/controllers/nvr.controller.d.ts +0 -3
- package/dist/server/src/api/controllers/nvr.controller.js +8 -38
- package/dist/server/src/api/controllers/nvr.controller.js.map +1 -1
- package/dist/server/src/api/controllers/plugins.controller.d.ts +9 -2
- package/dist/server/src/api/controllers/plugins.controller.js +156 -99
- package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
- package/dist/server/src/api/controllers/sensors.controller.d.ts +3 -1
- package/dist/server/src/api/controllers/sensors.controller.js +31 -0
- package/dist/server/src/api/controllers/sensors.controller.js.map +1 -1
- package/dist/server/src/api/database/index.d.ts +3 -2
- package/dist/server/src/api/database/index.js +12 -2
- package/dist/server/src/api/database/index.js.map +1 -1
- package/dist/server/src/api/database/migrations/v2-0-50.js +2 -1
- package/dist/server/src/api/database/migrations/v2-0-50.js.map +1 -1
- package/dist/server/src/api/database/migrations/v2-1-1.d.ts +3 -0
- package/dist/server/src/api/database/migrations/v2-1-1.js +35 -0
- package/dist/server/src/api/database/migrations/v2-1-1.js.map +1 -0
- package/dist/server/src/api/database/migrations/v2-1-4.d.ts +3 -0
- package/dist/server/src/api/database/migrations/v2-1-4.js +32 -0
- package/dist/server/src/api/database/migrations/v2-1-4.js.map +1 -0
- package/dist/server/src/api/database/record-schemas.d.ts +3 -0
- package/dist/server/src/api/database/record-schemas.js +1 -0
- package/dist/server/src/api/database/record-schemas.js.map +1 -1
- package/dist/server/src/api/database/types.d.ts +5 -0
- package/dist/server/src/api/routes/automations.routes.js +23 -1
- package/dist/server/src/api/routes/automations.routes.js.map +1 -1
- package/dist/server/src/api/routes/cameras.routes.js +15 -3
- package/dist/server/src/api/routes/cameras.routes.js.map +1 -1
- package/dist/server/src/api/routes/instances.routes.js +12 -1
- package/dist/server/src/api/routes/instances.routes.js.map +1 -1
- package/dist/server/src/api/routes/plugins.routes.js +36 -2
- package/dist/server/src/api/routes/plugins.routes.js.map +1 -1
- package/dist/server/src/api/routes/sensors.routes.js +23 -1
- package/dist/server/src/api/routes/sensors.routes.js.map +1 -1
- package/dist/server/src/api/schemas/automations.schema.d.ts +11 -0
- package/dist/server/src/api/schemas/automations.schema.js +7 -0
- package/dist/server/src/api/schemas/automations.schema.js.map +1 -1
- package/dist/server/src/api/schemas/cameras.schema.d.ts +33 -24
- package/dist/server/src/api/schemas/cameras.schema.js +18 -5
- package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
- package/dist/server/src/api/schemas/config.schema.d.ts +1 -0
- package/dist/server/src/api/schemas/config.schema.js +1 -0
- package/dist/server/src/api/schemas/config.schema.js.map +1 -1
- package/dist/server/src/api/schemas/instances.schema.d.ts +4 -0
- package/dist/server/src/api/schemas/instances.schema.js +3 -0
- package/dist/server/src/api/schemas/instances.schema.js.map +1 -1
- package/dist/server/src/api/schemas/plugins.schema.d.ts +11 -0
- package/dist/server/src/api/schemas/plugins.schema.js +6 -0
- package/dist/server/src/api/schemas/plugins.schema.js.map +1 -1
- package/dist/server/src/api/schemas/sensors.schema.d.ts +11 -0
- package/dist/server/src/api/schemas/sensors.schema.js +7 -0
- package/dist/server/src/api/schemas/sensors.schema.js.map +1 -1
- package/dist/server/src/api/schemas/users.schema.d.ts +12 -0
- package/dist/server/src/api/schemas/users.schema.js +4 -0
- package/dist/server/src/api/schemas/users.schema.js.map +1 -1
- package/dist/server/src/api/services/auth.service.js +28 -23
- package/dist/server/src/api/services/auth.service.js.map +1 -1
- package/dist/server/src/api/services/automations.service.js +36 -36
- package/dist/server/src/api/services/automations.service.js.map +1 -1
- package/dist/server/src/api/services/backup.service.js +29 -26
- package/dist/server/src/api/services/backup.service.js.map +1 -1
- package/dist/server/src/api/services/backupScheduler.service.js +1 -2
- package/dist/server/src/api/services/backupScheduler.service.js.map +1 -1
- package/dist/server/src/api/services/cameras.service.js +222 -173
- package/dist/server/src/api/services/cameras.service.js.map +1 -1
- package/dist/server/src/api/services/instances.service.d.ts +7 -0
- package/dist/server/src/api/services/instances.service.js +17 -0
- package/dist/server/src/api/services/instances.service.js.map +1 -1
- package/dist/server/src/api/services/mqtt.service.js +1 -2
- package/dist/server/src/api/services/mqtt.service.js.map +1 -1
- package/dist/server/src/api/services/notifications.service.d.ts +1 -0
- package/dist/server/src/api/services/notifications.service.js +3 -0
- package/dist/server/src/api/services/notifications.service.js.map +1 -1
- package/dist/server/src/api/services/plugins.service.js +1 -1
- package/dist/server/src/api/services/plugins.service.js.map +1 -1
- package/dist/server/src/api/services/remote.service.js +26 -25
- package/dist/server/src/api/services/remote.service.js.map +1 -1
- package/dist/server/src/api/services/sensors.service.d.ts +1 -0
- package/dist/server/src/api/services/sensors.service.js +1 -0
- package/dist/server/src/api/services/sensors.service.js.map +1 -1
- package/dist/server/src/api/services/server.service.js +9 -7
- package/dist/server/src/api/services/server.service.js.map +1 -1
- package/dist/server/src/api/services/shares.service.js +19 -14
- package/dist/server/src/api/services/shares.service.js.map +1 -1
- package/dist/server/src/api/services/users.service.js +159 -105
- package/dist/server/src/api/services/users.service.js.map +1 -1
- package/dist/server/src/api/services/workers.service.d.ts +1 -0
- package/dist/server/src/api/services/workers.service.js +55 -45
- package/dist/server/src/api/services/workers.service.js.map +1 -1
- package/dist/server/src/api/types/index.d.ts +38 -5
- package/dist/server/src/api/types/index.js.map +1 -1
- package/dist/server/src/api/utils/bulk.d.ts +9 -0
- package/dist/server/src/api/utils/bulk.js +27 -0
- package/dist/server/src/api/utils/bulk.js.map +1 -0
- package/dist/server/src/api/utils/cert.js +8 -0
- package/dist/server/src/api/utils/cert.js.map +1 -1
- package/dist/server/src/api/websocket/nsp/server.js +1 -1
- package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
- package/dist/server/src/automations/conditions/sensorState.js +9 -0
- package/dist/server/src/automations/conditions/sensorState.js.map +1 -1
- package/dist/server/src/camera/controller.d.ts +1 -1
- package/dist/server/src/camera/controller.js +12 -11
- package/dist/server/src/camera/controller.js.map +1 -1
- package/dist/server/src/camera/decoder/child.d.ts +1 -0
- package/dist/server/src/camera/decoder/child.js +14 -1
- package/dist/server/src/camera/decoder/child.js.map +1 -1
- package/dist/server/src/camera/decoder/debug/detection-record.d.ts +42 -0
- package/dist/server/src/camera/decoder/debug/detection-record.js +103 -0
- package/dist/server/src/camera/decoder/debug/detection-record.js.map +1 -0
- package/dist/server/src/camera/decoder/debug/perf-tracker.d.ts +20 -0
- package/dist/server/src/camera/decoder/debug/perf-tracker.js +82 -0
- package/dist/server/src/camera/decoder/debug/perf-tracker.js.map +1 -0
- package/dist/server/src/camera/decoder/detection-coordinator.d.ts +28 -6
- package/dist/server/src/camera/decoder/detection-coordinator.js +452 -76
- package/dist/server/src/camera/decoder/detection-coordinator.js.map +1 -1
- package/dist/server/src/camera/decoder/detection-pipeline.d.ts +7 -20
- package/dist/server/src/camera/decoder/detection-pipeline.js +67 -127
- package/dist/server/src/camera/decoder/detection-pipeline.js.map +1 -1
- package/dist/server/src/camera/decoder/event-manager.d.ts +42 -14
- package/dist/server/src/camera/decoder/event-manager.js +304 -217
- package/dist/server/src/camera/decoder/event-manager.js.map +1 -1
- package/dist/server/src/camera/decoder/event-thumbnailer.d.ts +5 -8
- package/dist/server/src/camera/decoder/event-thumbnailer.js +11 -21
- package/dist/server/src/camera/decoder/event-thumbnailer.js.map +1 -1
- package/dist/server/src/camera/decoder/frame-scaler.d.ts +2 -1
- package/dist/server/src/camera/decoder/frame-scaler.js +9 -0
- package/dist/server/src/camera/decoder/frame-scaler.js.map +1 -1
- package/dist/server/src/camera/decoder/moment-crop.d.ts +30 -0
- package/dist/server/src/camera/decoder/moment-crop.js +86 -0
- package/dist/server/src/camera/decoder/moment-crop.js.map +1 -0
- package/dist/server/src/camera/decoder/nvr-sink.d.ts +38 -0
- package/dist/server/src/camera/decoder/nvr-sink.js +36 -0
- package/dist/server/src/camera/decoder/nvr-sink.js.map +1 -0
- package/dist/server/src/camera/decoder/secondary-stage.d.ts +4 -2
- package/dist/server/src/camera/decoder/secondary-stage.js +58 -96
- package/dist/server/src/camera/decoder/secondary-stage.js.map +1 -1
- package/dist/server/src/camera/decoder/sources/analysis-source.d.ts +10 -0
- package/dist/server/src/camera/decoder/sources/analysis-source.js +2 -0
- package/dist/server/src/camera/decoder/sources/analysis-source.js.map +1 -0
- package/dist/server/src/camera/decoder/sources/{snapshot-source.d.ts → buffered-source.d.ts} +17 -5
- package/dist/server/src/camera/decoder/sources/{snapshot-source.js → buffered-source.js} +81 -24
- package/dist/server/src/camera/decoder/sources/buffered-source.js.map +1 -0
- package/dist/server/src/camera/decoder/sources/frame-source.d.ts +4 -7
- package/dist/server/src/camera/decoder/sources/frame-source.js +4 -7
- package/dist/server/src/camera/decoder/sources/frame-source.js.map +1 -1
- package/dist/server/src/camera/decoder/types.d.ts +1 -0
- package/dist/server/src/camera/decoder/types.js.map +1 -1
- package/dist/server/src/camera/decoder/worker.js +15 -3
- package/dist/server/src/camera/decoder/worker.js.map +1 -1
- package/dist/server/src/internal-bus.d.ts +1 -1
- package/dist/server/src/internal-bus.js.map +1 -1
- package/dist/server/src/main.js +1 -1
- package/dist/server/src/main.js.map +1 -1
- package/dist/server/src/manager/discoveryManager.js +0 -11
- package/dist/server/src/manager/discoveryManager.js.map +1 -1
- package/dist/server/src/manager/notificationManager.d.ts +1 -1
- package/dist/server/src/manager/notificationManager.js +60 -41
- package/dist/server/src/manager/notificationManager.js.map +1 -1
- package/dist/server/src/mqtt/bridge.d.ts +2 -0
- package/dist/server/src/mqtt/bridge.js +39 -35
- package/dist/server/src/mqtt/bridge.js.map +1 -1
- package/dist/server/src/plugins/nvr.d.ts +2 -0
- package/dist/server/src/plugins/nvr.js +17 -0
- package/dist/server/src/plugins/nvr.js.map +1 -0
- package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js +1 -1
- package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js.map +1 -1
- package/dist/server/src/plugins/runtime/python/storage.py +18 -1
- package/dist/server/src/plugins/runtime/python/store.py +7 -1
- package/dist/server/src/plugins/schema.d.ts +2 -1
- package/dist/server/src/plugins/schema.js +66 -6
- package/dist/server/src/plugins/schema.js.map +1 -1
- package/dist/server/src/plugins/store/storeFile.js +10 -2
- package/dist/server/src/plugins/store/storeFile.js.map +1 -1
- package/dist/server/src/remote/services/index.js +1 -1
- package/dist/server/src/remote/services/index.js.map +1 -1
- package/dist/server/src/rpc/interfaces/frameworker.d.ts +1 -0
- package/dist/server/src/rpc/interfaces/sensor.d.ts +2 -0
- package/dist/server/src/rpc/server.js +2 -0
- package/dist/server/src/rpc/server.js.map +1 -1
- package/dist/server/src/sensors/registry.js +7 -2
- package/dist/server/src/sensors/registry.js.map +1 -1
- package/dist/server/src/sensors/sensor.js +13 -1
- package/dist/server/src/sensors/sensor.js.map +1 -1
- package/dist/server/src/services/config/constants.js +1 -0
- package/dist/server/src/services/config/constants.js.map +1 -1
- package/dist/server/src/services/config/index.d.ts +5 -2
- package/dist/server/src/services/config/index.js +38 -5
- package/dist/server/src/services/config/index.js.map +1 -1
- package/dist/server/src/services/config/types.d.ts +4 -0
- package/dist/server/src/utils/path.d.ts +1 -0
- package/dist/server/src/utils/path.js +33 -0
- package/dist/server/src/utils/path.js.map +1 -1
- package/dist/server/src/workers/capabilities/frame-decoding.js +1 -1
- package/dist/server/src/workers/capabilities/frame-decoding.js.map +1 -1
- package/package.json +14 -14
- package/dist/interface/assets/Automations-DBjwNb3F.js +0 -2
- package/dist/interface/assets/Camera-BfvpjkW3.js +0 -2
- package/dist/interface/assets/Camera-yUrhUGmO.css +0 -1
- package/dist/interface/assets/Camview-DArvg940.js +0 -1
- package/dist/interface/assets/Camview-mLCaY0ld.css +0 -1
- package/dist/interface/assets/Config-Ccp5Acpv.js +0 -2
- package/dist/interface/assets/ConfigConditionSensorState-lI9dzHiT.js +0 -1
- package/dist/interface/assets/ConfigSensorValueInput-DW4er753.js +0 -1
- package/dist/interface/assets/ConfigTriggerSensor-vhwPNmPj.js +0 -1
- package/dist/interface/assets/Console-C6yUdEaM.js +0 -1
- package/dist/interface/assets/CuiAutomationRunsDialog-C4i7y1Re.js +0 -1
- package/dist/interface/assets/CuiBottomSheet-C2zU_iyj.js +0 -1
- package/dist/interface/assets/CuiCameraPipCard-CZ-oDxzy.js +0 -1
- package/dist/interface/assets/CuiCameraRecordings-BHj7CUE7.css +0 -1
- package/dist/interface/assets/CuiCameraRecordings-mY_giLEi.js +0 -1
- package/dist/interface/assets/CuiLightControl-CEpcKss7.js +0 -1
- package/dist/interface/assets/CuiMarkdownContent-C2uBepC1.js +0 -1
- package/dist/interface/assets/CuiMarkdownContent-QxLR7_qs.js +0 -1
- package/dist/interface/assets/CuiPluginCard-1dSDwOCf.js +0 -2
- package/dist/interface/assets/CuiRecordingsGrid-COHg3ZCT.css +0 -1
- package/dist/interface/assets/CuiRecordingsGrid-CkwE3jcR.js +0 -1
- package/dist/interface/assets/CuiSchema-DKATswRc.js +0 -1
- package/dist/interface/assets/Faces-BeeZrFV-.js +0 -1
- package/dist/interface/assets/FirstSteps-DWT6Tt_T.js +0 -1
- package/dist/interface/assets/Instances-CJlFo8ni.js +0 -1
- package/dist/interface/assets/Menu-DP0aMICW.js +0 -1
- package/dist/interface/assets/OAuthDeviceFlow-CcSkntGk.js +0 -1
- package/dist/interface/assets/Plugin-8dbt_8iG.js +0 -1
- package/dist/interface/assets/Plugin-BLqyZrJP.css +0 -1
- package/dist/interface/assets/PluginDetail-C9dR9jrw.js +0 -2
- package/dist/interface/assets/PluginSearch-BVAtgBZs.js +0 -1
- package/dist/interface/assets/PluginSearch-HEf_6CrR.js +0 -2
- package/dist/interface/assets/Plugins-CQJhji6o.js +0 -1
- package/dist/interface/assets/Recordings-AD78njIA.js +0 -1
- package/dist/interface/assets/SensorEdit-rC02usEO.js +0 -1
- package/dist/interface/assets/Sensors-CFfII3-e.css +0 -1
- package/dist/interface/assets/Sensors-Vwr7MYz6.js +0 -2
- package/dist/interface/assets/SettingsAccount-BBsivYFf.js +0 -1
- package/dist/interface/assets/SettingsAccount-CN9Xcgt-.js +0 -1
- package/dist/interface/assets/SettingsAppearance-Qh1cw9iX.js +0 -1
- package/dist/interface/assets/SettingsBackup-DNDDU6a1.js +0 -1
- package/dist/interface/assets/SettingsList-tFKUMnsB.js +0 -1
- package/dist/interface/assets/SettingsMqtt-BYVV1Mew.js +0 -1
- package/dist/interface/assets/SettingsRecordings-B4DDJSKe.js +0 -1
- package/dist/interface/assets/SettingsRemote-Bib7kXto.js +0 -1
- package/dist/interface/assets/SettingsSystem-DIe7CSgH.js +0 -2
- package/dist/interface/assets/Terminal-DuJHIlLe.js +0 -4
- package/dist/interface/assets/VersionsHandler-D9sDkGXx.js +0 -7
- package/dist/interface/assets/Workers-fV_NEZ7U.js +0 -2
- package/dist/interface/assets/api-Draww2tC.js +0 -2
- package/dist/interface/assets/api-eELh8KCM.css +0 -2
- package/dist/interface/assets/automations-BfsdbSPC.js +0 -1
- package/dist/interface/assets/cameras.schema-DMY4SYte.js +0 -1
- package/dist/interface/assets/chevron-right-AhqYJOFy.js +0 -1
- package/dist/interface/assets/cloudHandoff-BQsDy9yp.js +0 -1
- package/dist/interface/assets/esm-B1LZs0VG.js +0 -1
- package/dist/interface/assets/esm-BB4SBaek.js +0 -1
- package/dist/interface/assets/esm-BGA8Ydne.js +0 -1
- package/dist/interface/assets/esm-BHI_KH7L.js +0 -1
- package/dist/interface/assets/esm-BQ0EuuZe.js +0 -1
- package/dist/interface/assets/esm-Bfb-tuKt.js +0 -1
- package/dist/interface/assets/esm-BzR_6ma5.js +0 -1
- package/dist/interface/assets/esm-CCIqNfsh.js +0 -1
- package/dist/interface/assets/esm-CSMN-8o1.js +0 -1
- package/dist/interface/assets/esm-CxWbrBxP.js +0 -1
- package/dist/interface/assets/esm-DBTrmJK6.js +0 -1
- package/dist/interface/assets/esm-DRswZUWf.js +0 -1
- package/dist/interface/assets/esm-Dk7gxPHs.js +0 -1
- package/dist/interface/assets/esm-P0RCkAtk.js +0 -1
- package/dist/interface/assets/main-CV7qCZ5i.js +0 -3
- package/dist/interface/assets/mqtt-BPfLOT7L.js +0 -1
- package/dist/interface/assets/native-Cr4dkfqy.js +0 -1
- package/dist/interface/assets/native-D1Vvlhds.js +0 -1
- package/dist/interface/assets/plugin-937qnq7E.js +0 -1
- package/dist/interface/assets/plugins-BU7TQh_f.js +0 -1
- package/dist/interface/assets/sensorIcons-DtJEfoU4.js +0 -1
- package/dist/interface/assets/sensors-DjgfMS8S.js +0 -1
- package/dist/interface/assets/types-BGMvANrz.js +0 -1
- package/dist/interface/assets/useBackupRestore-E3pVEOvA.js +0 -1
- package/dist/interface/assets/useCardSelection-DGPLn1ZH.js +0 -1
- package/dist/interface/assets/useUserOptions-BL53bECb.js +0 -1
- package/dist/interface/assets/vendor-cameraui-DojVuZ2L.js +0 -2326
- package/dist/interface/assets/vendor-core-ZDcIKAw9.js +0 -1
- package/dist/interface/assets/web-BQtDHNew.js +0 -1
- package/dist/interface/assets/web-BY9kjyTj.js +0 -1
- package/dist/interface/assets/web-BcQtDSat.js +0 -1
- package/dist/interface/assets/web-BgrRQzLO.js +0 -1
- package/dist/interface/assets/web-BjKL51lK-CJYmHsnY.js +0 -1
- package/dist/interface/assets/web-BxvsJylk.js +0 -1
- package/dist/interface/assets/web-C-3iwDhG.js +0 -1
- package/dist/interface/assets/web-C9XOwu07.js +0 -1
- package/dist/interface/assets/web-CPCxRDtw.js +0 -1
- package/dist/interface/assets/web-D0U5B2i2.js +0 -1
- package/dist/interface/assets/web-DtxjXJdS.js +0 -1
- package/dist/interface/assets/web-Duf-oV4e.js +0 -1
- package/dist/interface/assets/web-jy45nBi_.js +0 -1
- package/dist/interface/assets/web-vWx_-Iq5.js +0 -1
- package/dist/interface/assets/workbox-window.prod.es5-Dlqax_Po.js +0 -1
- package/dist/server/src/api/utils/moveFiles.d.ts +0 -4
- package/dist/server/src/api/utils/moveFiles.js +0 -37
- package/dist/server/src/api/utils/moveFiles.js.map +0 -1
- package/dist/server/src/camera/decoder/sources/snapshot-source.js.map +0 -1
- package/dist/server/src/camera/decoder/stationary-suppressor.d.ts +0 -38
- package/dist/server/src/camera/decoder/stationary-suppressor.js +0 -308
- package/dist/server/src/camera/decoder/stationary-suppressor.js.map +0 -1
|
@@ -15,24 +15,35 @@ import { DETECTION_SENSOR_TYPES } from '../../sensors/types.js';
|
|
|
15
15
|
import { normalizeZone } from '../utils/filter.js';
|
|
16
16
|
import { AudioDetectionLoop } from './audio-loop.js';
|
|
17
17
|
import { CascadeManager } from './cascade-manager.js';
|
|
18
|
+
import { detectionRecord } from './debug/detection-record.js';
|
|
19
|
+
import { PerfTracker } from './debug/perf-tracker.js';
|
|
18
20
|
import { DetectionPipeline } from './detection-pipeline.js';
|
|
19
21
|
import { DwellManager } from './dwell-manager.js';
|
|
20
|
-
import { DetectionEventManager } from './event-manager.js';
|
|
22
|
+
import { DetectionEventManager, MOMENT_RANK_ATTRIBUTE, MOMENT_RANK_OBJECT } from './event-manager.js';
|
|
21
23
|
import { EventThumbnailer } from './event-thumbnailer.js';
|
|
22
24
|
import { FrameScaler } from './frame-scaler.js';
|
|
25
|
+
import { directionBetween, directionOf, MOMENT_FORMATS, MOMENT_QUALITY, momentWindow, unionBox } from './moment-crop.js';
|
|
26
|
+
import { normalizePlateText } from './plate-vote.js';
|
|
23
27
|
import { isVideoInputSpec, PluginRegistry } from './plugin-registry.js';
|
|
24
28
|
import { PtzAutotracker } from './ptz/autotracker.js';
|
|
25
29
|
import { ReconnectBackoff } from './reconnect-backoff.js';
|
|
26
30
|
import { SecondaryStage } from './secondary-stage.js';
|
|
27
31
|
import { FrameSource } from './sources/frame-source.js';
|
|
28
|
-
import { StationarySuppressor } from './stationary-suppressor.js';
|
|
29
32
|
import { DETECT_TIMEOUT_MS, ensureDetectionBoxes, MOTION_WIDTH_MAP } from './types.js';
|
|
33
|
+
const MOMENT_EVENTS = new Set(['objectEntered', 'objectWoke', 'objectRecovered', 'bestShotUpdated']);
|
|
34
|
+
const MOMENT_MOVING_SPEED = 0.05;
|
|
35
|
+
const MOMENT_ATTRIBUTE_MIN_AREA = 600;
|
|
30
36
|
const DEFAULT_CASCADE_TIMEOUT = 10;
|
|
31
37
|
const OBJECT_DWELL_SECONDS = 2;
|
|
32
38
|
const SECONDARY_BBOX_TTL_MS = 2000;
|
|
33
39
|
const CADENCE_MIN_SAMPLES = 5;
|
|
34
40
|
const CADENCE_OUTLIER_BAND = 2.5;
|
|
35
41
|
const CADENCE_OUTLIER_RESEED = 5;
|
|
42
|
+
const IDLE_TICK_MS = 200;
|
|
43
|
+
const ACTIVE_TICK_MS = 100;
|
|
44
|
+
const MOTION_INTERVAL_MS = 200;
|
|
45
|
+
const TICK_SLACK_MS = 20;
|
|
46
|
+
const MAIN_STREAM_HOLD_MS = 5000;
|
|
36
47
|
let DetectionCoordinator = class DetectionCoordinator {
|
|
37
48
|
config;
|
|
38
49
|
proxy;
|
|
@@ -41,29 +52,38 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
41
52
|
frameScaler;
|
|
42
53
|
plugins = new PluginRegistry();
|
|
43
54
|
pipeline;
|
|
44
|
-
stationary;
|
|
45
55
|
secondaries;
|
|
46
56
|
thumbnailer;
|
|
47
57
|
audioLoop;
|
|
48
58
|
ptzAutotracker;
|
|
49
59
|
eventManager;
|
|
50
60
|
cascade = new CascadeManager();
|
|
61
|
+
worldSpans = new Set();
|
|
62
|
+
activeSensorTriggerTypes = new Map();
|
|
63
|
+
secondaryBboxSeen = new Map();
|
|
64
|
+
faceIdentities = new Set();
|
|
65
|
+
platesSeen = new Set();
|
|
66
|
+
classifierLabels = new Map();
|
|
67
|
+
feedingSensors = new Map();
|
|
51
68
|
dwell = new DwellManager();
|
|
52
69
|
videoBackoff = new ReconnectBackoff();
|
|
70
|
+
// debugging
|
|
71
|
+
perf = new PerfTracker();
|
|
72
|
+
hqUpgrade;
|
|
73
|
+
mainStreamActive = false;
|
|
74
|
+
idleSince = 0;
|
|
75
|
+
lastMotionAt = 0;
|
|
53
76
|
loopRunning = false;
|
|
54
77
|
loopPromise;
|
|
55
78
|
videoStopPromise;
|
|
56
79
|
adHocVideoLoop = false;
|
|
57
80
|
processingExternalSecondary = false;
|
|
58
|
-
cascadeUnsubscribe;
|
|
59
|
-
dwellUnsubscribe;
|
|
60
|
-
activeSensorTriggerTypes = new Map();
|
|
61
|
-
secondaryBboxSeen = new Map();
|
|
62
|
-
feedingSensors = new Set();
|
|
63
81
|
lastObjectCallAt = 0;
|
|
64
82
|
objectIntervalMs = 0;
|
|
65
83
|
objectIntervalSamples = 0;
|
|
66
84
|
objectIntervalOutliers = 0;
|
|
85
|
+
cascadeUnsubscribe;
|
|
86
|
+
dwellUnsubscribe;
|
|
67
87
|
currentDetectionState = {};
|
|
68
88
|
constructor(config, proxy, logger) {
|
|
69
89
|
this.config = config;
|
|
@@ -72,7 +92,6 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
72
92
|
const frameSourceConfig = {
|
|
73
93
|
streamUrl: config.streamUrl,
|
|
74
94
|
snapshotUrl: config.snapshotUrl,
|
|
75
|
-
fps: this.config.frameWorkerSettings.fps,
|
|
76
95
|
decoder: this.config.frameWorkerSettings.decoder,
|
|
77
96
|
};
|
|
78
97
|
if (config.controllerSnapshotSourceId) {
|
|
@@ -88,13 +107,18 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
88
107
|
this.frameSource = new FrameSource(frameSourceConfig, logger);
|
|
89
108
|
this.frameScaler = new FrameScaler(null, logger);
|
|
90
109
|
this.pipeline = new DetectionPipeline(config.zones, config.detectionSettings);
|
|
91
|
-
this.stationary = new StationarySuppressor(logger);
|
|
92
110
|
this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, logger);
|
|
93
111
|
if (config.lines.length > 0) {
|
|
94
112
|
this.pipeline.updateLines(config.lines, this.videoAspectRatio);
|
|
95
113
|
}
|
|
96
114
|
this.eventManager = new DetectionEventManager(config.cameraId, this.proxy, this.logger);
|
|
115
|
+
this.eventManager.updateNvrRpc(config.nvrRpc);
|
|
97
116
|
this.eventManager.onEventEnd(() => this.handleEventEnded());
|
|
117
|
+
this.eventManager.onSegmentClosed(() => {
|
|
118
|
+
if (!this.dwell.hasActive() && this.worldSpans.size === 0) {
|
|
119
|
+
this.eventManager.forceEndActiveEvent();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
98
122
|
this.thumbnailer = new EventThumbnailer({
|
|
99
123
|
frameSource: this.frameSource,
|
|
100
124
|
frameScaler: this.frameScaler,
|
|
@@ -102,7 +126,19 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
102
126
|
logger,
|
|
103
127
|
decoder: this.config.frameWorkerSettings.decoder,
|
|
104
128
|
}, config.availableSources);
|
|
105
|
-
this.thumbnailer.sync(this.
|
|
129
|
+
this.thumbnailer.sync(this.mainStreamAnalysisWanted);
|
|
130
|
+
// debugging
|
|
131
|
+
detectionRecord.sources({
|
|
132
|
+
roles: config.availableSources?.map((s) => s.role),
|
|
133
|
+
mainStreamAnalysis: this.config.frameWorkerSettings.mainStreamAnalysis === true,
|
|
134
|
+
hasMainStream: this.thumbnailer.hasMainStream,
|
|
135
|
+
cascade: this.cascadeEnabled,
|
|
136
|
+
cascadeTimeout: this.cascadeTimeoutSeconds,
|
|
137
|
+
motionTimeout: this.config.detectionSettings.motion.timeout,
|
|
138
|
+
audioTimeout: this.config.detectionSettings.audio.timeout,
|
|
139
|
+
suppressStatic: this.config.detectionSettings.object.suppressStatic ?? true,
|
|
140
|
+
motionResolution: this.motionResolution,
|
|
141
|
+
});
|
|
106
142
|
this.audioLoop = new AudioDetectionLoop({
|
|
107
143
|
cameraId: config.cameraId,
|
|
108
144
|
getPlugin: () => this.plugins.get(SensorType.Audio),
|
|
@@ -120,11 +156,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
120
156
|
});
|
|
121
157
|
this.cascadeUnsubscribe = this.cascade.onChange((event) => {
|
|
122
158
|
if (event.type === 'activated') {
|
|
123
|
-
this.pipeline.setReidHitCounterMax(this.cascadeTimeoutSeconds * this.targetFps);
|
|
124
159
|
this.startAdHocVideoLoopIfNeeded();
|
|
125
160
|
}
|
|
126
161
|
else {
|
|
127
|
-
this.pipeline.setReidHitCounterMax(0);
|
|
128
162
|
this.stopAdHocVideoLoopIfIdle();
|
|
129
163
|
this.handleCascadeDeactivated();
|
|
130
164
|
}
|
|
@@ -146,7 +180,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
146
180
|
this.writeSensorProperties(event.sensorId, { detected: false, blocked: false });
|
|
147
181
|
}
|
|
148
182
|
this.activeSensorTriggerTypes.delete(event.sensorId);
|
|
149
|
-
|
|
183
|
+
this.eventManager.processResults(this.buildSnapshot());
|
|
184
|
+
if (!this.dwell.hasActive() && this.worldSpans.size === 0 && !this.eventManager.hasActiveSegment()) {
|
|
150
185
|
this.eventManager.forceEndActiveEvent();
|
|
151
186
|
}
|
|
152
187
|
}
|
|
@@ -176,7 +211,11 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
176
211
|
}
|
|
177
212
|
updateFrameWorkerSettings(settings) {
|
|
178
213
|
this.config.frameWorkerSettings = settings;
|
|
179
|
-
this.thumbnailer.sync(
|
|
214
|
+
this.thumbnailer.sync(this.mainStreamAnalysisWanted);
|
|
215
|
+
}
|
|
216
|
+
updateNvrRpc(namespace) {
|
|
217
|
+
this.config.nvrRpc = namespace;
|
|
218
|
+
this.eventManager.updateNvrRpc(namespace);
|
|
180
219
|
}
|
|
181
220
|
updateInterfaceSettings(settings) {
|
|
182
221
|
this.config.interfaceSettings = settings;
|
|
@@ -196,12 +235,11 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
196
235
|
this.eventManager.destroy();
|
|
197
236
|
this.adHocVideoLoop = false;
|
|
198
237
|
this.frameScaler.dispose();
|
|
199
|
-
this.pipeline.cleanup();
|
|
200
238
|
this.currentDetectionState = {};
|
|
201
239
|
}
|
|
202
240
|
async onSensorAdded(sensor) {
|
|
203
241
|
if (DETECTION_SENSOR_TYPES.has(sensor.sensorType)) {
|
|
204
|
-
this.feedingSensors.
|
|
242
|
+
this.feedingSensors.set(sensor.sensorId, sensor.sensorType);
|
|
205
243
|
}
|
|
206
244
|
if (DETECTION_SENSOR_TYPES.has(sensor.sensorType) && sensor.requiresFrames) {
|
|
207
245
|
await this.registerDetectionPluginInternal(sensor);
|
|
@@ -215,8 +253,22 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
215
253
|
}
|
|
216
254
|
}
|
|
217
255
|
async onSensorRemoved(sensorId) {
|
|
218
|
-
|
|
219
|
-
|
|
256
|
+
this.classifierLabels.delete(sensorId);
|
|
257
|
+
const sensorType = this.feedingSensors.get(sensorId);
|
|
258
|
+
if (sensorType === SensorType.Face)
|
|
259
|
+
this.faceIdentities.clear();
|
|
260
|
+
if (sensorType === SensorType.LicensePlate)
|
|
261
|
+
this.platesSeen.clear();
|
|
262
|
+
if (sensorType !== undefined && this.feedingSensors.delete(sensorId)) {
|
|
263
|
+
// a sensor removed mid-segment misses the cascade-end clear, so the
|
|
264
|
+
// recognized lists must reset here or they survive the re-register
|
|
265
|
+
this.writeSensorProperties(sensorId, {
|
|
266
|
+
detected: false,
|
|
267
|
+
detections: [],
|
|
268
|
+
...(sensorType === SensorType.Face ? { identities: [] } : {}),
|
|
269
|
+
...(sensorType === SensorType.LicensePlate ? { plates: [] } : {}),
|
|
270
|
+
...(sensorType === SensorType.Classifier ? { labels: [] } : {}),
|
|
271
|
+
});
|
|
220
272
|
}
|
|
221
273
|
await this.removeDetectionPluginBySensor(sensorId);
|
|
222
274
|
this.ptzAutotracker.unbind(sensorId);
|
|
@@ -264,7 +316,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
264
316
|
this.ingestAssistedObjects(sensorId, objects);
|
|
265
317
|
// processExternal only adapts the shape, external boxes get no real tracking
|
|
266
318
|
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
267
|
-
await this.runSecondariesAndThumbnails(peeked.frame, objectDetections, results);
|
|
319
|
+
await this.runSecondariesAndThumbnails({ frame: peeked.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
268
320
|
this.ingestResultsForAllSecondaries(results);
|
|
269
321
|
const snapshot = this.buildSnapshot();
|
|
270
322
|
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
@@ -288,7 +340,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
288
340
|
// real boxes: crop the object like the frame pipeline, so face and
|
|
289
341
|
// plate secondaries run on the subject instead of the whole scene
|
|
290
342
|
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
291
|
-
await this.runSecondariesAndThumbnails(fetched.frame, objectDetections, results);
|
|
343
|
+
await this.runSecondariesAndThumbnails({ frame: fetched.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
|
|
292
344
|
this.ingestResultsForAllSecondaries(results);
|
|
293
345
|
const snapshot = this.buildSnapshot();
|
|
294
346
|
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
@@ -297,11 +349,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
297
349
|
this.eventManager.processResults(snapshot);
|
|
298
350
|
}
|
|
299
351
|
else {
|
|
300
|
-
const objectDetections = this.pipeline.processExternal(objects.detections);
|
|
301
352
|
const sceneJpeg = await this.secondaries.detectFullFrame(fetched.frame, results);
|
|
302
353
|
try {
|
|
303
|
-
|
|
304
|
-
results.thumbnails = await this.secondaries.generateThumbnails(fetched.frame, this.frameScaler, false, objectDetections, results);
|
|
354
|
+
results.thumbnails = await this.secondaries.generateThumbnails(fetched.frame, this.frameScaler, results);
|
|
305
355
|
}
|
|
306
356
|
catch (error) {
|
|
307
357
|
this.logger.error('Thumbnail generation error:', error);
|
|
@@ -356,7 +406,8 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
356
406
|
}
|
|
357
407
|
this.dwell.refresh(dwellKey, timeoutSeconds);
|
|
358
408
|
if (action === 'activate' && this.cascadeEnabled) {
|
|
359
|
-
|
|
409
|
+
// trigger edges never repeat, the cascade covers the trigger's dwell
|
|
410
|
+
this.cascade.triggerMomentary(Math.max(timeoutSeconds, this.cascadeTimeoutSeconds));
|
|
360
411
|
}
|
|
361
412
|
this.eventManager.processResults(this.buildSnapshot());
|
|
362
413
|
// no loop frame to encode from, fetch async (no-op if already captured)
|
|
@@ -377,6 +428,12 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
377
428
|
hasPlugin(sensorType) {
|
|
378
429
|
return this.plugins.has(sensorType);
|
|
379
430
|
}
|
|
431
|
+
externalObjectSpanActive() {
|
|
432
|
+
const objectPlugin = this.plugins.get(SensorType.Object);
|
|
433
|
+
if (!objectPlugin || objectPlugin.requiresFrames)
|
|
434
|
+
return false;
|
|
435
|
+
return this.dwell.isActive(objectPlugin.sensorId);
|
|
436
|
+
}
|
|
380
437
|
get cascadeEnabled() {
|
|
381
438
|
return this.config.detectionSettings.cascadeDetection !== false;
|
|
382
439
|
}
|
|
@@ -413,22 +470,26 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
413
470
|
for (const plugin of this.plugins.getAll(SensorType.Classifier)) {
|
|
414
471
|
clearTargets.push(plugin.sensorId);
|
|
415
472
|
}
|
|
473
|
+
this.faceIdentities.clear();
|
|
474
|
+
this.platesSeen.clear();
|
|
475
|
+
this.classifierLabels.clear();
|
|
476
|
+
const faceSensorId = this.plugins.get(SensorType.Face)?.sensorId;
|
|
477
|
+
const plateSensorId = this.plugins.get(SensorType.LicensePlate)?.sensorId;
|
|
478
|
+
const classifierSensorIds = new Set(this.plugins.getAll(SensorType.Classifier).map((p) => p.sensorId));
|
|
416
479
|
for (const sensorId of clearTargets) {
|
|
417
|
-
this.writeSensorProperties(sensorId, {
|
|
480
|
+
this.writeSensorProperties(sensorId, {
|
|
481
|
+
detected: false,
|
|
482
|
+
detections: [],
|
|
483
|
+
...(sensorId === faceSensorId ? { identities: [] } : {}),
|
|
484
|
+
...(sensorId === plateSensorId ? { plates: [] } : {}),
|
|
485
|
+
...(classifierSensorIds.has(sensorId) ? { labels: [] } : {}),
|
|
486
|
+
});
|
|
418
487
|
this.secondaryBboxSeen.delete(sensorId);
|
|
419
488
|
}
|
|
420
489
|
// segment ends here, the event itself continues while motion is active
|
|
421
490
|
this.eventManager.processResults(this.buildSnapshot());
|
|
422
491
|
}
|
|
423
492
|
handleEventEnded() {
|
|
424
|
-
if (this.config.detectionSettings.object.suppressStatic ?? true) {
|
|
425
|
-
this.stationary.retainAcrossEvent((trackIds) => this.pipeline.retainTracks(trackIds));
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
this.pipeline.cleanup();
|
|
429
|
-
this.stationary.clear();
|
|
430
|
-
}
|
|
431
|
-
this.stationary.resetEventState();
|
|
432
493
|
this.activeSensorTriggerTypes.clear();
|
|
433
494
|
this.currentDetectionState = {};
|
|
434
495
|
}
|
|
@@ -451,15 +512,16 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
451
512
|
};
|
|
452
513
|
this.proxy.publish(NamespaceManager.sensorCameraViewNamespaces(this.config.cameraId).sensorWriteSubject, msg);
|
|
453
514
|
}
|
|
454
|
-
buildSnapshot() {
|
|
515
|
+
buildSnapshot(at = Date.now()) {
|
|
455
516
|
const cs = this.currentDetectionState;
|
|
456
517
|
return {
|
|
457
518
|
hasCascadeTrigger: this.cascade.isActive,
|
|
519
|
+
hasOpenSpans: this.worldSpans.size > 0 || this.externalObjectSpanActive(),
|
|
458
520
|
motion: cs.motion ? { detected: cs.motion.detected ?? false } : undefined,
|
|
459
521
|
audio: cs.audio ? { detected: cs.audio.detected ?? false, detections: cs.audio.detections ?? [] } : undefined,
|
|
460
522
|
cascadeTriggered: cs.cascadeTriggered,
|
|
461
523
|
sensorTriggers: [...new Set(this.activeSensorTriggerTypes.values())],
|
|
462
|
-
objects: cs.object?.detected ?
|
|
524
|
+
objects: cs.object?.detected ? cs.object.detections : [],
|
|
463
525
|
faces: cs.face?.detections ?? [],
|
|
464
526
|
faceEmbeddingModel: cs.faceEmbeddingModel,
|
|
465
527
|
plates: cs.licensePlate?.detections ?? [],
|
|
@@ -472,7 +534,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
472
534
|
clipEmbeddingModel: cs.clipEmbeddingModel,
|
|
473
535
|
thumbnails: undefined,
|
|
474
536
|
lineCrossings: cs.lineCrossings,
|
|
475
|
-
timestamp:
|
|
537
|
+
timestamp: at,
|
|
476
538
|
segmentTimeout: 10,
|
|
477
539
|
expectedEndTime: this.dwell.maxExpiry(),
|
|
478
540
|
detectionZones: this.getNormalizedDetectionZones(),
|
|
@@ -532,7 +594,6 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
532
594
|
return properties;
|
|
533
595
|
return { ...properties, detections: filtered, detected: filtered.length > 0 };
|
|
534
596
|
}
|
|
535
|
-
// PTZ-suppression gating happens upstream, callers decide what gets in here
|
|
536
597
|
ingestDetectionResult(sensorType, sensorId, properties, pluginId) {
|
|
537
598
|
if (sensorType === SensorType.Audio) {
|
|
538
599
|
properties = this.applyAudioConfidence(properties);
|
|
@@ -543,7 +604,12 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
543
604
|
const motionTimeout = this.config.detectionSettings.motion.timeout;
|
|
544
605
|
this.dwell.refresh(sensorId, motionTimeout);
|
|
545
606
|
if (this.cascadeEnabled) {
|
|
546
|
-
|
|
607
|
+
// a frame-based motion sensor re-arms on every motion frame, so its
|
|
608
|
+
// short tail is fine; an external sensor fires one edge and goes
|
|
609
|
+
// quiet, that edge must arm the cascade for the whole belief window
|
|
610
|
+
const framePlugin = this.plugins.get(SensorType.Motion);
|
|
611
|
+
const frameBased = framePlugin?.requiresFrames === true && framePlugin.sensorId === sensorId;
|
|
612
|
+
this.cascade.triggerMomentary(frameBased ? this.cascadeTimeoutSeconds : Math.max(motionTimeout, this.cascadeTimeoutSeconds));
|
|
547
613
|
}
|
|
548
614
|
}
|
|
549
615
|
else if (sensorType === SensorType.Audio && detected) {
|
|
@@ -557,7 +623,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
557
623
|
// the dwell bridges single missed frames; anchored stationary tracks
|
|
558
624
|
// don't refresh it, so parked objects can't hold events open
|
|
559
625
|
const detections = properties.detections ?? [];
|
|
560
|
-
if (
|
|
626
|
+
if (detections.length > 0) {
|
|
561
627
|
this.dwell.refresh(sensorId, OBJECT_DWELL_SECONDS);
|
|
562
628
|
// a confirmed object re-arms the cascade like motion does: a time-based
|
|
563
629
|
// motion sensor (ONVIF cool-down) must not blind detection and split
|
|
@@ -574,7 +640,6 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
574
640
|
this.currentDetectionState.object.detected = false;
|
|
575
641
|
this.currentDetectionState.object.detections = [];
|
|
576
642
|
}
|
|
577
|
-
this.stationary.logSuppressedOnce(detections);
|
|
578
643
|
}
|
|
579
644
|
}
|
|
580
645
|
// dwell owns detected/blocked/lastTriggered for these types, a no-detection
|
|
@@ -711,8 +776,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
711
776
|
for (const plugin of this.plugins.getAll(SensorType.Classifier)) {
|
|
712
777
|
this.clearSecondaryBboxes(plugin.sensorId);
|
|
713
778
|
}
|
|
714
|
-
|
|
715
|
-
this.stationary.dropForCameraMove();
|
|
779
|
+
this.pipeline.notifyCameraMove();
|
|
716
780
|
}
|
|
717
781
|
clearSecondaryBboxes(sensorId) {
|
|
718
782
|
this.writeSensorProperties(sensorId, { detections: [] });
|
|
@@ -744,6 +808,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
744
808
|
}
|
|
745
809
|
this.logger.debug('Stopping video detection loop');
|
|
746
810
|
this.loopRunning = false;
|
|
811
|
+
this.worldSpans.clear();
|
|
812
|
+
this.mainStreamActive = false;
|
|
813
|
+
this.idleSince = 0;
|
|
747
814
|
const doStop = async () => {
|
|
748
815
|
await this.frameSource.stop();
|
|
749
816
|
await this.loopPromise;
|
|
@@ -776,27 +843,51 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
776
843
|
try {
|
|
777
844
|
await this.frameSource.start();
|
|
778
845
|
this.frameScaler.updateHardwareContext(this.frameSource.hardwareContext);
|
|
846
|
+
// the hardware context exists only after start, re-evaluate the HQ session
|
|
847
|
+
this.thumbnailer.sync(this.mainStreamAnalysisWanted);
|
|
779
848
|
this.logger.debug('Stream connected, processing frames...');
|
|
780
849
|
this.videoBackoff.reset();
|
|
781
850
|
let frameCount = 0;
|
|
782
851
|
let lastFrameId = -1;
|
|
783
852
|
while (this.loopRunning) {
|
|
853
|
+
const tickStart = Date.now();
|
|
784
854
|
const snap = await this.frameSource.nextFrame(lastFrameId);
|
|
785
855
|
if (!snap)
|
|
786
856
|
break; // source ended (stop or EOF)
|
|
787
857
|
lastFrameId = snap.id;
|
|
788
858
|
frameCount++;
|
|
859
|
+
// motion always reads the low stream: switching its input resolution
|
|
860
|
+
// would reset the background model on every transition
|
|
861
|
+
const analysis = await this.acquireAnalysisFrame(snap.frame);
|
|
862
|
+
// debugging
|
|
863
|
+
detectionRecord.setFrame(analysis.isMainStream ? 'main' : 'low', analysis.frame.width, analysis.frame.height);
|
|
789
864
|
try {
|
|
790
|
-
await this.processRawFrame(snap.frame);
|
|
865
|
+
await this.processRawFrame(analysis, snap.frame);
|
|
791
866
|
}
|
|
792
867
|
finally {
|
|
793
868
|
try {
|
|
869
|
+
if (this.hqUpgrade) {
|
|
870
|
+
this.hqUpgrade.frame[Symbol.dispose]?.();
|
|
871
|
+
this.hqUpgrade = undefined;
|
|
872
|
+
}
|
|
873
|
+
if (analysis.frame !== snap.frame)
|
|
874
|
+
analysis.frame[Symbol.dispose]?.();
|
|
794
875
|
snap.frame[Symbol.dispose]?.();
|
|
795
876
|
}
|
|
796
877
|
catch {
|
|
797
878
|
// best-effort
|
|
798
879
|
}
|
|
799
880
|
}
|
|
881
|
+
this.updateStreamState();
|
|
882
|
+
if (this.mainStreamActive)
|
|
883
|
+
this.perf.activeTicks++;
|
|
884
|
+
else
|
|
885
|
+
this.perf.idleTicks++;
|
|
886
|
+
this.perf.report(this.logger);
|
|
887
|
+
const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
|
|
888
|
+
if (remaining > 0)
|
|
889
|
+
await sleep(remaining);
|
|
890
|
+
this.perf.loopMs += Date.now() - tickStart;
|
|
800
891
|
}
|
|
801
892
|
const streamError = this.frameSource.lastError;
|
|
802
893
|
await this.frameSource.stop();
|
|
@@ -825,25 +916,85 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
825
916
|
this.frameScaler.clearCache();
|
|
826
917
|
this.logger.debug('Detection loop ended');
|
|
827
918
|
}
|
|
828
|
-
|
|
919
|
+
get mainStreamAvailable() {
|
|
920
|
+
if (!this.thumbnailer.hasMainStream)
|
|
921
|
+
return false;
|
|
922
|
+
return this.mainStreamAnalysisWanted;
|
|
923
|
+
}
|
|
924
|
+
get mainStreamAnalysisWanted() {
|
|
925
|
+
return this.config.frameWorkerSettings.mainStreamAnalysis === true || this.frameSource.hardwareContext !== null;
|
|
926
|
+
}
|
|
927
|
+
async acquireAnalysisFrame(lowFrame) {
|
|
928
|
+
if (this.mainStreamActive) {
|
|
929
|
+
const t0 = Date.now();
|
|
930
|
+
try {
|
|
931
|
+
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
932
|
+
if (main) {
|
|
933
|
+
this.perf.mainFrames++;
|
|
934
|
+
this.perf.decodeMs += Date.now() - t0;
|
|
935
|
+
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
this.logger.debug('Main stream frame unavailable:', error);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return { frame: lowFrame, scaler: this.frameScaler, isMainStream: false };
|
|
943
|
+
}
|
|
944
|
+
async acquireOpeningHqFrame() {
|
|
945
|
+
if (!this.mainStreamAvailable)
|
|
946
|
+
return undefined;
|
|
947
|
+
const t0 = Date.now();
|
|
948
|
+
try {
|
|
949
|
+
const main = await this.thumbnailer.acquireHqFrame(0);
|
|
950
|
+
if (main) {
|
|
951
|
+
this.perf.mainFrames++;
|
|
952
|
+
this.perf.decodeMs += Date.now() - t0;
|
|
953
|
+
return { frame: main.frame, scaler: main.scaler, isMainStream: true };
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
catch (error) {
|
|
957
|
+
this.logger.debug('HQ frame unavailable for the opening tick:', error);
|
|
958
|
+
}
|
|
959
|
+
return undefined;
|
|
960
|
+
}
|
|
961
|
+
updateStreamState() {
|
|
962
|
+
if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment()) {
|
|
963
|
+
this.idleSince = 0;
|
|
964
|
+
if (!this.mainStreamActive && this.mainStreamAvailable) {
|
|
965
|
+
this.mainStreamActive = true;
|
|
966
|
+
this.perf.switches++;
|
|
967
|
+
this.logger.debug('Analysis switched to the main stream');
|
|
968
|
+
}
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (!this.mainStreamActive)
|
|
972
|
+
return;
|
|
973
|
+
if (this.idleSince === 0) {
|
|
974
|
+
this.idleSince = Date.now();
|
|
975
|
+
}
|
|
976
|
+
else if (Date.now() - this.idleSince >= MAIN_STREAM_HOLD_MS) {
|
|
977
|
+
this.mainStreamActive = false;
|
|
978
|
+
this.idleSince = 0;
|
|
979
|
+
this.perf.switches++;
|
|
980
|
+
this.logger.debug('Analysis back on the low-resolution stream');
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async processRawFrame(analysis, motionRawFrame) {
|
|
829
984
|
if (!this.loopRunning || !this.plugins.hasAny())
|
|
830
985
|
return;
|
|
831
986
|
try {
|
|
832
|
-
await this.processDetection(
|
|
987
|
+
await this.processDetection(analysis, motionRawFrame);
|
|
833
988
|
}
|
|
834
989
|
catch (error) {
|
|
835
990
|
this.logger.error('Detection processing error:', error);
|
|
836
991
|
}
|
|
837
992
|
}
|
|
838
|
-
async processDetection(
|
|
993
|
+
async processDetection(analysis, motionRawFrame) {
|
|
839
994
|
const t0 = Date.now();
|
|
840
995
|
let motionDetected = false;
|
|
841
996
|
let objectDetections = [];
|
|
842
997
|
const results = { timestamp: t0 };
|
|
843
|
-
// keeps dead tracks from expiring during the cascade window
|
|
844
|
-
if (this.cascade.isActive) {
|
|
845
|
-
this.pipeline.refreshReid();
|
|
846
|
-
}
|
|
847
998
|
// while the PTZ repositions: motion and object keep running so the
|
|
848
999
|
// background model and track ids survive the pan, but nothing below
|
|
849
1000
|
// reaches dwell/events until the settle window ends
|
|
@@ -852,12 +1003,20 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
852
1003
|
// object detection, not motion
|
|
853
1004
|
const motionPlugin = this.plugins.get(SensorType.Motion);
|
|
854
1005
|
if (motionPlugin?.requiresFrames) {
|
|
855
|
-
|
|
1006
|
+
// motion keeps its own rate, a background diff gains nothing from the active tick speed
|
|
1007
|
+
const motionDue = t0 - this.lastMotionAt >= MOTION_INTERVAL_MS - TICK_SLACK_MS;
|
|
1008
|
+
const motionScaleStart = Date.now();
|
|
1009
|
+
const motionFrame = motionDue ? await this.scaleForMotion(motionRawFrame) : undefined;
|
|
1010
|
+
if (motionDue)
|
|
1011
|
+
this.perf.scaleMs += Date.now() - motionScaleStart;
|
|
856
1012
|
if (!this.loopRunning)
|
|
857
1013
|
return;
|
|
858
1014
|
if (motionFrame) {
|
|
1015
|
+
this.lastMotionAt = t0;
|
|
859
1016
|
try {
|
|
1017
|
+
const motionInferStart = Date.now();
|
|
860
1018
|
const result = await motionPlugin.proxy.detectMotion(motionFrame);
|
|
1019
|
+
this.perf.inferMs += Date.now() - motionInferStart;
|
|
861
1020
|
if (!this.loopRunning)
|
|
862
1021
|
return;
|
|
863
1022
|
if (ptzSuppressed) {
|
|
@@ -899,12 +1058,16 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
899
1058
|
const objectPlugin = this.plugins.get(SensorType.Object);
|
|
900
1059
|
const shouldDetectObjects = objectPlugin?.requiresFrames === true && (ptzSuppressed || !this.cascadeEnabled || this.cascade.isActive);
|
|
901
1060
|
if (shouldDetectObjects) {
|
|
902
|
-
const
|
|
1061
|
+
const scaleStart = Date.now();
|
|
1062
|
+
const objectFrame = await this.scaleForObject(analysis.frame, analysis.scaler);
|
|
1063
|
+
this.perf.scaleMs += Date.now() - scaleStart;
|
|
903
1064
|
if (!this.loopRunning)
|
|
904
1065
|
return;
|
|
905
1066
|
if (objectFrame) {
|
|
906
1067
|
try {
|
|
1068
|
+
const inferStart = Date.now();
|
|
907
1069
|
const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
|
|
1070
|
+
this.perf.inferMs += Date.now() - inferStart;
|
|
908
1071
|
if (!this.loopRunning)
|
|
909
1072
|
return;
|
|
910
1073
|
this.trackDetectionCadence();
|
|
@@ -912,14 +1075,26 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
912
1075
|
// Kalman state; the pose delta keeps predictions stable across pans
|
|
913
1076
|
const poseDelta = this.ptzAutotracker.consumePoseDelta();
|
|
914
1077
|
const detected = FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
|
|
915
|
-
const pipelineResult = this.pipeline.process(detected,
|
|
1078
|
+
const pipelineResult = this.pipeline.process(detected, poseDelta);
|
|
1079
|
+
// the tick that opens a span analysed the low stream (the switch lands
|
|
1080
|
+
// next tick), its picture work is worth a one-off HQ decode
|
|
1081
|
+
if (this.updateWorldSpans(pipelineResult) && !analysis.isMainStream) {
|
|
1082
|
+
this.hqUpgrade = await this.acquireOpeningHqFrame();
|
|
1083
|
+
}
|
|
1084
|
+
await this.captureMoments(pipelineResult, this.hqUpgrade ?? analysis, t0);
|
|
1085
|
+
// open spans and a lingering segment keep the cascade armed: the
|
|
1086
|
+
// world can only observe absence or a reappearance while it still
|
|
1087
|
+
// receives ticks
|
|
1088
|
+
if (this.cascadeEnabled && (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())) {
|
|
1089
|
+
this.cascade.triggerMomentary(this.cascadeTimeoutSeconds);
|
|
1090
|
+
}
|
|
916
1091
|
// track id churn is invisible in the event log without this
|
|
917
1092
|
if (pipelineResult.created.length > 0 || pipelineResult.removed.length > 0) {
|
|
918
1093
|
const born = pipelineResult.created.map((id) => {
|
|
919
1094
|
const t = pipelineResult.tracked.find((d) => d.trackId === id);
|
|
920
1095
|
return `${t?.label ?? 'unknown'}#${id}`;
|
|
921
1096
|
});
|
|
922
|
-
const died = pipelineResult.removed.map((id) => `#${id}
|
|
1097
|
+
const died = pipelineResult.removed.map((id) => `#${id}`);
|
|
923
1098
|
this.logger.trace(`[tracker] ${born.length ? `born: ${born.join(', ')}` : ''}${born.length && died.length ? ' — ' : ''}${died.length ? `died: ${died.join(', ')}` : ''}`);
|
|
924
1099
|
}
|
|
925
1100
|
// extrapolated (Kalman-only) tracks smooth single-frame misses in
|
|
@@ -974,7 +1149,11 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
974
1149
|
if (!this.loopRunning)
|
|
975
1150
|
return;
|
|
976
1151
|
if (objectDetections.length > 0) {
|
|
977
|
-
|
|
1152
|
+
const images = this.hqUpgrade ?? analysis;
|
|
1153
|
+
await this.runSecondariesAndThumbnails(images, objectDetections, results);
|
|
1154
|
+
if (!this.loopRunning)
|
|
1155
|
+
return;
|
|
1156
|
+
await this.captureAttributeMoment(results, images, t0);
|
|
978
1157
|
}
|
|
979
1158
|
if (!this.loopRunning)
|
|
980
1159
|
return;
|
|
@@ -984,7 +1163,7 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
984
1163
|
// messages already carry them
|
|
985
1164
|
if (!this.loopRunning)
|
|
986
1165
|
return;
|
|
987
|
-
const snapshot = this.buildSnapshot();
|
|
1166
|
+
const snapshot = this.buildSnapshot(t0);
|
|
988
1167
|
if (results.thumbnails && results.thumbnails.length > 0) {
|
|
989
1168
|
snapshot.thumbnails = results.thumbnails;
|
|
990
1169
|
}
|
|
@@ -994,7 +1173,9 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
994
1173
|
let hqThumbAttached = false;
|
|
995
1174
|
if (wantsEventThumb) {
|
|
996
1175
|
try {
|
|
997
|
-
const
|
|
1176
|
+
const thumbStart = Date.now();
|
|
1177
|
+
const thumb = await this.thumbnailer.captureEventThumbnail((this.hqUpgrade ?? analysis).frame);
|
|
1178
|
+
this.perf.jpegMs += Date.now() - thumbStart;
|
|
998
1179
|
if (thumb.jpeg)
|
|
999
1180
|
snapshot.eventThumbnail = thumb.jpeg;
|
|
1000
1181
|
hqThumbAttached = thumb.fromHq;
|
|
@@ -1004,25 +1185,29 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1004
1185
|
}
|
|
1005
1186
|
}
|
|
1006
1187
|
this.eventManager.processResults(snapshot);
|
|
1188
|
+
// spans can outlive every trigger dwell (gate case); once spans, dwells
|
|
1189
|
+
// and the segment linger are all quiet the event ends here
|
|
1190
|
+
if (this.worldSpans.size === 0 && !this.dwell.hasActive() && !this.eventManager.hasActiveSegment()) {
|
|
1191
|
+
this.eventManager.forceEndActiveEvent();
|
|
1192
|
+
}
|
|
1007
1193
|
// covers the window where the HQ source wasn't ready at event start
|
|
1008
1194
|
if (wantsEventThumb && !hqThumbAttached) {
|
|
1009
1195
|
this.thumbnailer.upgradeEventThumbnailAsync();
|
|
1010
1196
|
}
|
|
1011
1197
|
}
|
|
1012
|
-
async runSecondariesAndThumbnails(
|
|
1013
|
-
objectDetections = this.stationary.excludeSealed(objectDetections);
|
|
1198
|
+
async runSecondariesAndThumbnails(analysis, objectDetections, results) {
|
|
1014
1199
|
if (objectDetections.length === 0)
|
|
1015
1200
|
return;
|
|
1016
|
-
|
|
1201
|
+
this.perf.objects += objectDetections.length;
|
|
1202
|
+
const secondaryStart = Date.now();
|
|
1203
|
+
await this.secondaries.detect(analysis.frame, analysis.scaler, objectDetections, results);
|
|
1204
|
+
this.perf.secondaryMs += Date.now() - secondaryStart;
|
|
1017
1205
|
try {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
finally {
|
|
1024
|
-
hq?.frame[Symbol.dispose]?.();
|
|
1025
|
-
}
|
|
1206
|
+
const jpegStart = Date.now();
|
|
1207
|
+
results.thumbnails = await this.secondaries.generateThumbnails(analysis.frame, analysis.scaler, results);
|
|
1208
|
+
this.perf.jpegMs += Date.now() - jpegStart;
|
|
1209
|
+
this.perf.faces += results.face?.detections?.length ?? 0;
|
|
1210
|
+
this.perf.plates += results.licensePlate?.detections?.length ?? 0;
|
|
1026
1211
|
}
|
|
1027
1212
|
catch (error) {
|
|
1028
1213
|
this.logger.error('Thumbnail generation error:', error);
|
|
@@ -1035,18 +1220,31 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1035
1220
|
}
|
|
1036
1221
|
const facePlugin = this.plugins.get(SensorType.Face);
|
|
1037
1222
|
if (facePlugin) {
|
|
1223
|
+
// no mid-segment clear: a turned-away head yields a no-face frame and
|
|
1224
|
+
// would flap the list, departure is reflected when the segment ends
|
|
1225
|
+
for (const detection of results.face.detections) {
|
|
1226
|
+
if (detection.identity)
|
|
1227
|
+
this.faceIdentities.add(detection.identity);
|
|
1228
|
+
}
|
|
1038
1229
|
this.ingestDetectionResult(SensorType.Face, facePlugin.sensorId, {
|
|
1039
1230
|
detected: results.face.detected,
|
|
1040
1231
|
detections: results.face.detections,
|
|
1232
|
+
identities: [...this.faceIdentities].sort(),
|
|
1041
1233
|
});
|
|
1042
1234
|
}
|
|
1043
1235
|
}
|
|
1044
1236
|
if (results.licensePlate) {
|
|
1045
1237
|
const lpdPlugin = this.plugins.get(SensorType.LicensePlate);
|
|
1046
1238
|
if (lpdPlugin) {
|
|
1239
|
+
for (const detection of results.licensePlate.detections) {
|
|
1240
|
+
const plate = detection.plateText ? normalizePlateText(detection.plateText) || detection.plateText : '';
|
|
1241
|
+
if (plate)
|
|
1242
|
+
this.platesSeen.add(plate);
|
|
1243
|
+
}
|
|
1047
1244
|
this.ingestDetectionResult(SensorType.LicensePlate, lpdPlugin.sensorId, {
|
|
1048
1245
|
detected: results.licensePlate.detected,
|
|
1049
1246
|
detections: results.licensePlate.detections,
|
|
1247
|
+
plates: [...this.platesSeen].sort(),
|
|
1050
1248
|
});
|
|
1051
1249
|
}
|
|
1052
1250
|
}
|
|
@@ -1054,9 +1252,21 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1054
1252
|
for (const [pluginId, classifierResult] of Object.entries(results.classifiers)) {
|
|
1055
1253
|
const plugin = this.plugins.getAll(SensorType.Classifier).find((p) => p.pluginId === pluginId);
|
|
1056
1254
|
if (plugin) {
|
|
1255
|
+
let seen = this.classifierLabels.get(plugin.sensorId);
|
|
1256
|
+
if (!seen) {
|
|
1257
|
+
seen = new Set();
|
|
1258
|
+
this.classifierLabels.set(plugin.sensorId, seen);
|
|
1259
|
+
}
|
|
1260
|
+
for (const detection of classifierResult.detections) {
|
|
1261
|
+
// the specific answer wins (subAttribute, e.g. the bird species)
|
|
1262
|
+
const label = detection.subAttribute || detection.attribute || detection.label;
|
|
1263
|
+
if (label)
|
|
1264
|
+
seen.add(label);
|
|
1265
|
+
}
|
|
1057
1266
|
this.ingestDetectionResult(SensorType.Classifier, plugin.sensorId, {
|
|
1058
1267
|
detected: classifierResult.detected,
|
|
1059
1268
|
detections: classifierResult.detections,
|
|
1269
|
+
labels: [...seen].sort(),
|
|
1060
1270
|
}, pluginId);
|
|
1061
1271
|
}
|
|
1062
1272
|
}
|
|
@@ -1082,6 +1292,173 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1082
1292
|
return true;
|
|
1083
1293
|
return false;
|
|
1084
1294
|
}
|
|
1295
|
+
updateWorldSpans(result) {
|
|
1296
|
+
let opened = false;
|
|
1297
|
+
const suppressStatic = this.config.detectionSettings.object.suppressStatic ?? true;
|
|
1298
|
+
for (const event of result.events) {
|
|
1299
|
+
const obj = event.object;
|
|
1300
|
+
const recoveredRelevant = event.eventType === 'objectRecovered' && (obj.state !== 'stationary' || !suppressStatic);
|
|
1301
|
+
if (event.eventType === 'objectEntered' || event.eventType === 'objectWoke' || recoveredRelevant) {
|
|
1302
|
+
if (!this.worldSpans.has(obj.trackId))
|
|
1303
|
+
opened = true;
|
|
1304
|
+
this.worldSpans.add(obj.trackId);
|
|
1305
|
+
}
|
|
1306
|
+
else if (event.eventType === 'objectDeparted' || event.eventType === 'objectLost' || (event.eventType === 'objectSettled' && suppressStatic)) {
|
|
1307
|
+
this.worldSpans.delete(obj.trackId);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return opened;
|
|
1311
|
+
}
|
|
1312
|
+
async captureMoments(result, analysis, at) {
|
|
1313
|
+
let subject;
|
|
1314
|
+
let bestScore = -1;
|
|
1315
|
+
let trigger = '';
|
|
1316
|
+
for (const event of result.events) {
|
|
1317
|
+
const obj = event.object;
|
|
1318
|
+
// a moment is worth a picture when a span opens or the view got better;
|
|
1319
|
+
// closing events report absence, there is nothing to show
|
|
1320
|
+
if (!MOMENT_EVENTS.has(event.eventType))
|
|
1321
|
+
continue;
|
|
1322
|
+
// and only for a track that holds a span: a tentative first sighting or a
|
|
1323
|
+
// stationary recovery opens nothing, so its picture is never handed over
|
|
1324
|
+
if (!this.worldSpans.has(obj.trackId))
|
|
1325
|
+
continue;
|
|
1326
|
+
const score = obj.confidence * Math.sqrt(obj.width * obj.height);
|
|
1327
|
+
if (score > bestScore) {
|
|
1328
|
+
bestScore = score;
|
|
1329
|
+
subject = obj;
|
|
1330
|
+
trigger = event.eventType;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
// one frame per tick, not one per object: they share the picture
|
|
1334
|
+
if (!subject)
|
|
1335
|
+
return;
|
|
1336
|
+
if (!this.eventManager.wantsMoment(MOMENT_RANK_OBJECT, bestScore, analysis.isMainStream ? 'main' : 'low', at))
|
|
1337
|
+
return;
|
|
1338
|
+
await this.captureMoment(subject, bestScore, trigger, result.tracked, analysis, at);
|
|
1339
|
+
}
|
|
1340
|
+
async captureMoment(subject, score, trigger, tracked, analysis, at) {
|
|
1341
|
+
const box = { x: subject.x, y: subject.y, width: subject.width, height: subject.height };
|
|
1342
|
+
const moving = tracked.filter((d) => (d.trackSpeed ?? 0) >= MOMENT_MOVING_SPEED).map((d) => d.box);
|
|
1343
|
+
const target = { subject: box, base: unionBox([box, ...moving]), hint: directionOf(subject.velocityX, subject.velocityY) };
|
|
1344
|
+
const rendered = await this.renderMoment(target, analysis);
|
|
1345
|
+
if (!rendered)
|
|
1346
|
+
return;
|
|
1347
|
+
const stream = analysis.isMainStream ? 'main' : 'low';
|
|
1348
|
+
this.eventManager.offerMoment({ strip: rendered.strip, card: rendered.card, capturedAt: at, score, rank: MOMENT_RANK_OBJECT, stream });
|
|
1349
|
+
// debugging
|
|
1350
|
+
await this.recordMoment(target, rendered, analysis, `${subject.label}-${subject.trackId}`, score, trigger, at);
|
|
1351
|
+
}
|
|
1352
|
+
async captureAttributeMoment(results, analysis, at) {
|
|
1353
|
+
const candidates = [];
|
|
1354
|
+
// same rule as the object rank: only a span-holding subject hands over a
|
|
1355
|
+
// picture. A face or plate riding on a settled bystander (the parked car
|
|
1356
|
+
// at the kerb) must not anchor the segment of an unrelated event
|
|
1357
|
+
const spanHolder = (parentTrackId) => parentTrackId !== undefined && this.worldSpans.has(parentTrackId);
|
|
1358
|
+
for (const face of results.face?.detections ?? []) {
|
|
1359
|
+
const f = face;
|
|
1360
|
+
if (!spanHolder(f.parentTrackId))
|
|
1361
|
+
continue;
|
|
1362
|
+
candidates.push({ label: `face:${f.identity ?? 'unknown'}`, box: f.box, parentBox: f.parentBox, confidence: f.confidence });
|
|
1363
|
+
}
|
|
1364
|
+
for (const plate of results.licensePlate?.detections ?? []) {
|
|
1365
|
+
const p = plate;
|
|
1366
|
+
if (!p.plateText || !spanHolder(p.parentTrackId))
|
|
1367
|
+
continue;
|
|
1368
|
+
candidates.push({ label: `plate:${normalizePlateText(p.plateText)}`, box: p.box, parentBox: p.parentBox, confidence: p.confidence });
|
|
1369
|
+
}
|
|
1370
|
+
// one picture per tick, never one per subject: with several faces the most
|
|
1371
|
+
// legible one frames the shot, the rest stay their own attribute tiles
|
|
1372
|
+
let best;
|
|
1373
|
+
let bestScore = -1;
|
|
1374
|
+
let bestCard;
|
|
1375
|
+
for (const candidate of candidates) {
|
|
1376
|
+
const cut = results.thumbnails?.find((t) => t.label === candidate.label);
|
|
1377
|
+
// a subject cut off by the frame edge makes a poor card
|
|
1378
|
+
if (cut?.onEdge)
|
|
1379
|
+
continue;
|
|
1380
|
+
// a few-pixel face or plate carries no information even at the crop's max
|
|
1381
|
+
// upscale: it stays an attribute tile, the anchor keeps the object shot
|
|
1382
|
+
const areaPx = candidate.box.width * analysis.frame.width * candidate.box.height * analysis.frame.height;
|
|
1383
|
+
if (areaPx < MOMENT_ATTRIBUTE_MIN_AREA)
|
|
1384
|
+
continue;
|
|
1385
|
+
const score = candidate.confidence * Math.sqrt(candidate.box.width * candidate.box.height);
|
|
1386
|
+
if (score > bestScore) {
|
|
1387
|
+
bestScore = score;
|
|
1388
|
+
best = candidate;
|
|
1389
|
+
bestCard = cut?.jpeg;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
// ask before cutting: the same face rides along on every tick of a segment
|
|
1393
|
+
// and re-encoding for a picture that loses anyway is pure cost
|
|
1394
|
+
if (!best || !this.eventManager.wantsMoment(MOMENT_RANK_ATTRIBUTE, bestScore, analysis.isMainStream ? 'main' : 'low', at))
|
|
1395
|
+
return;
|
|
1396
|
+
const target = {
|
|
1397
|
+
subject: best.box,
|
|
1398
|
+
hint: best.parentBox ? directionBetween(best.box, best.parentBox) : undefined,
|
|
1399
|
+
};
|
|
1400
|
+
// the attribute tile was cut from this frame with this very target, so the
|
|
1401
|
+
// card is already encoded; only the 16:9 companion is still missing
|
|
1402
|
+
const rendered = await this.renderMoment(target, analysis, bestCard);
|
|
1403
|
+
if (!rendered)
|
|
1404
|
+
return;
|
|
1405
|
+
const stream = analysis.isMainStream ? 'main' : 'low';
|
|
1406
|
+
this.eventManager.offerMoment({ strip: rendered.strip, card: rendered.card, capturedAt: at, score: bestScore, rank: MOMENT_RANK_ATTRIBUTE, stream });
|
|
1407
|
+
// debugging
|
|
1408
|
+
await this.recordMoment(target, rendered, analysis, best.label.replace(':', '-'), bestScore, 'attributeFound', at);
|
|
1409
|
+
}
|
|
1410
|
+
async renderMoment(target, analysis, card) {
|
|
1411
|
+
const start = Date.now();
|
|
1412
|
+
const rendered = new Map();
|
|
1413
|
+
const windows = {};
|
|
1414
|
+
if (card)
|
|
1415
|
+
rendered.set('card', card);
|
|
1416
|
+
try {
|
|
1417
|
+
for (const format of MOMENT_FORMATS) {
|
|
1418
|
+
const window = momentWindow(target, analysis.frame.width, analysis.frame.height, format);
|
|
1419
|
+
if (!window)
|
|
1420
|
+
continue;
|
|
1421
|
+
windows[format.name] = window;
|
|
1422
|
+
if (rendered.has(format.name))
|
|
1423
|
+
continue;
|
|
1424
|
+
const jpeg = await analysis.scaler.cropWindowToJPEG(analysis.frame, window, format.width, format.height, MOMENT_QUALITY);
|
|
1425
|
+
if (jpeg)
|
|
1426
|
+
rendered.set(format.name, jpeg);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
catch (error) {
|
|
1430
|
+
this.logger.debug('Moment crop failed:', error);
|
|
1431
|
+
}
|
|
1432
|
+
this.perf.jpegMs += Date.now() - start;
|
|
1433
|
+
const strip = rendered.get('strip');
|
|
1434
|
+
return strip ? { strip, card: rendered.get('card'), windows } : null;
|
|
1435
|
+
}
|
|
1436
|
+
// debugging
|
|
1437
|
+
async recordMoment(target, rendered, analysis, subjectName, score, trigger, at) {
|
|
1438
|
+
if (!detectionRecord.active)
|
|
1439
|
+
return;
|
|
1440
|
+
const stream = analysis.isMainStream ? 'main' : 'low';
|
|
1441
|
+
const detail = `${subjectName}_${stream}_s${score.toFixed(3)}`;
|
|
1442
|
+
try {
|
|
1443
|
+
const frameJpeg = await analysis.scaler.frameToJPEG(analysis.frame, analysis.frame.width, 85);
|
|
1444
|
+
detectionRecord.moment({
|
|
1445
|
+
at,
|
|
1446
|
+
trigger,
|
|
1447
|
+
score,
|
|
1448
|
+
subject: target.subject,
|
|
1449
|
+
base: target.base,
|
|
1450
|
+
windows: rendered.windows,
|
|
1451
|
+
pictures: {
|
|
1452
|
+
strip: detectionRecord.picture(at, 'moment', `strip_${detail}`, rendered.strip),
|
|
1453
|
+
card: rendered.card ? detectionRecord.picture(at, 'moment', `card_${detail}`, rendered.card) : undefined,
|
|
1454
|
+
},
|
|
1455
|
+
frame: frameJpeg ? detectionRecord.picture(at, 'frame', detail, frameJpeg) : undefined,
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
catch (error) {
|
|
1459
|
+
this.logger.debug('Moment record failed:', error);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1085
1462
|
async scaleForMotion(rawFrame) {
|
|
1086
1463
|
const motionPlugin = this.plugins.get(SensorType.Motion);
|
|
1087
1464
|
if (!motionPlugin?.requiresFrames)
|
|
@@ -1116,24 +1493,23 @@ let DetectionCoordinator = class DetectionCoordinator {
|
|
|
1116
1493
|
this.objectIntervalSamples++;
|
|
1117
1494
|
if (this.objectIntervalSamples < CADENCE_MIN_SAMPLES)
|
|
1118
1495
|
return;
|
|
1119
|
-
this.pipeline.syncDetectionCadence(this.objectIntervalMs);
|
|
1120
1496
|
}
|
|
1121
|
-
async scaleForObject(rawFrame) {
|
|
1497
|
+
async scaleForObject(rawFrame, scaler) {
|
|
1122
1498
|
const objectPlugin = this.plugins.get(SensorType.Object);
|
|
1123
1499
|
if (!objectPlugin?.requiresFrames)
|
|
1124
1500
|
return undefined;
|
|
1125
|
-
return this.scaleFrameForPlugin(rawFrame, objectPlugin);
|
|
1501
|
+
return this.scaleFrameForPlugin(rawFrame, objectPlugin, scaler);
|
|
1126
1502
|
}
|
|
1127
|
-
async scaleFrameForPlugin(rawFrame, plugin) {
|
|
1503
|
+
async scaleFrameForPlugin(rawFrame, plugin, scaler = this.frameScaler) {
|
|
1128
1504
|
const inputSpec = plugin.modelSpec?.input;
|
|
1129
1505
|
if (!isVideoInputSpec(inputSpec))
|
|
1130
1506
|
return undefined;
|
|
1131
|
-
const letterboxed = await
|
|
1507
|
+
const letterboxed = await scaler.letterboxToSpec(rawFrame, inputSpec);
|
|
1132
1508
|
if (!letterboxed)
|
|
1133
1509
|
return undefined;
|
|
1134
1510
|
return {
|
|
1135
|
-
model:
|
|
1136
|
-
tracking:
|
|
1511
|
+
model: scaler.toVideoFrameData(letterboxed.padded, 'model'),
|
|
1512
|
+
tracking: scaler.toVideoFrameData(letterboxed.inner, 'tracking'),
|
|
1137
1513
|
geometry: letterboxed.geometry,
|
|
1138
1514
|
};
|
|
1139
1515
|
}
|