@camera.ui/server 2.1.9 → 2.1.10

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.
Files changed (349) hide show
  1. package/dist/interface/assets/{404-BEb5gvkW.js → 404-BLNSh-sA.js} +1 -1
  2. package/dist/interface/assets/{About-BKkjuIVJ.js → About-ZakRzon7.js} +1 -1
  3. package/dist/interface/assets/Automation-DoVhkuVY.js +3 -0
  4. package/dist/interface/assets/AutomationImport-DVxK1R1y.js +2 -0
  5. package/dist/interface/assets/{AutomationStore-DKhnExZn.js → AutomationStore-DaNO9_9S.js} +2 -2
  6. package/dist/interface/assets/{AutomationStoreDetail-DmqqEOir.js → AutomationStoreDetail-CTDCEaIz.js} +2 -2
  7. package/dist/interface/assets/Automations-mdnUitWx.js +2 -0
  8. package/dist/interface/assets/Camera-B3cv5IVC.js +2 -0
  9. package/dist/interface/assets/Camera-LSq1bWmA.css +1 -0
  10. package/dist/interface/assets/{CameraConsole-DVpyGhx8.js → CameraConsole-C32X5I91.js} +2 -2
  11. package/dist/interface/assets/Cameras-Drv48-Eb.js +652 -0
  12. package/dist/interface/assets/Camview-Bvy39jSt.js +1 -0
  13. package/dist/interface/assets/{Config-DeUec_TN.js → Config-B-LDdaS6.js} +1 -1
  14. package/dist/interface/assets/{ConfigActionCameraControl-DWHg-YEU.js → ConfigActionCameraControl-As2qXvnd.js} +1 -1
  15. package/dist/interface/assets/{ConfigActionDelay-C8nNA7TC.js → ConfigActionDelay-Cj3yWfEV.js} +1 -1
  16. package/dist/interface/assets/{ConfigActionHttp-Dc97gItH.js → ConfigActionHttp-PlS5vI0v.js} +1 -1
  17. package/dist/interface/assets/{ConfigActionImageInput-DpOGqlzA.js → ConfigActionImageInput-BHflOA-T.js} +1 -1
  18. package/dist/interface/assets/{ConfigActionMqtt-D5DJ21oe.js → ConfigActionMqtt-DByOwVPe.js} +1 -1
  19. package/dist/interface/assets/{ConfigActionNotification-4wKfanGB.js → ConfigActionNotification-D_8_lp96.js} +1 -1
  20. package/dist/interface/assets/{ConfigActionNotificationControl-jSgxCZmV.js → ConfigActionNotificationControl-ClFMom6m.js} +1 -1
  21. package/dist/interface/assets/{ConfigActionOutput-C8Esieni.js → ConfigActionOutput-YTAw-Are.js} +1 -1
  22. package/dist/interface/assets/{ConfigActionPlugin-C_cv60d3.js → ConfigActionPlugin-lF2IH2Bo.js} +1 -1
  23. package/dist/interface/assets/{ConfigActionSensor-CFIKoJ3-.js → ConfigActionSensor-DQZW_Jxr.js} +1 -1
  24. package/dist/interface/assets/{ConfigActionSnapshot-BqaTwtN3.js → ConfigActionSnapshot-CHxIQ5J0.js} +1 -1
  25. package/dist/interface/assets/{ConfigActionVariable-De9RCGU4.js → ConfigActionVariable-0VLGuhwP.js} +1 -1
  26. package/dist/interface/assets/{ConfigConditionIfElse-CeNDIucw.js → ConfigConditionIfElse-D-Ibn8QL.js} +1 -1
  27. package/dist/interface/assets/{ConfigConditionSensorState-C_Q9gU23.js → ConfigConditionSensorState-BKSGYRdv.js} +1 -1
  28. package/dist/interface/assets/{ConfigConditionSwitch-DR63IGcD.js → ConfigConditionSwitch-CzkEyWb1.js} +1 -1
  29. package/dist/interface/assets/{ConfigConditionTime-C9iuSxxe.js → ConfigConditionTime-BFKsDI_t.js} +1 -1
  30. package/dist/interface/assets/{ConfigSensorValueInput-DnMlFzil.js → ConfigSensorValueInput-yTiHhlXG.js} +1 -1
  31. package/dist/interface/assets/ConfigTriggerDetection-DFTgRaE0.js +1 -0
  32. package/dist/interface/assets/ConfigTriggerGeofence-B2Cqi5Id.js +1 -0
  33. package/dist/interface/assets/{ConfigTriggerManual-DPkRlUqD.js → ConfigTriggerManual-BDK6OYxg.js} +1 -1
  34. package/dist/interface/assets/{ConfigTriggerMqtt-kd65tGJE.js → ConfigTriggerMqtt-BZO14RrW.js} +1 -1
  35. package/dist/interface/assets/{ConfigTriggerSchedule-D6MoVDPc.js → ConfigTriggerSchedule-sMkzMLgn.js} +1 -1
  36. package/dist/interface/assets/{ConfigTriggerSensor-AMQM6f2r.js → ConfigTriggerSensor-D6W_exDW.js} +1 -1
  37. package/dist/interface/assets/{ConfigTriggerSystem-CNBgtDF1.js → ConfigTriggerSystem-D4cx1GqI.js} +1 -1
  38. package/dist/interface/assets/{ConfigTriggerWebhook-B70DobSi.js → ConfigTriggerWebhook-DxZziGkL.js} +1 -1
  39. package/dist/interface/assets/Console-DjA3ZPU_.js +1 -0
  40. package/dist/interface/assets/CuiAutomationRunsDialog-B4ABK72A.js +1 -0
  41. package/dist/interface/assets/CuiBottomSheet-D1zDWsoK.js +1 -0
  42. package/dist/interface/assets/{CuiBottomSheet-D5N8X1K-.js → CuiBottomSheet-N9MHq4yZ.js} +1 -1
  43. package/dist/interface/assets/CuiCameraPipCard-CnWs2fAC.js +1 -0
  44. package/dist/interface/assets/CuiCameraRecordings-CahW-f8_.js +1 -0
  45. package/dist/interface/assets/{CuiCameraShares-DMHwZEf1.js → CuiCameraShares-B2RdPWWh.js} +1 -1
  46. package/dist/interface/assets/CuiCameraTable-4ikGv8QA.js +1 -0
  47. package/dist/interface/assets/{CuiCameraTable-AnEPu3XS.js → CuiCameraTable-BKBvhRDc.js} +2 -2
  48. package/dist/interface/assets/CuiChartTable-CqKo74Tf.css +1 -0
  49. package/dist/interface/assets/CuiChartTable-Lvz9Zq0k.js +1 -0
  50. package/dist/interface/assets/{CuiConsole-CqEvNGe_.js → CuiConsole-DUswVhFC.js} +2 -2
  51. package/dist/interface/assets/CuiDataTable-CIP95jVF.js +1 -0
  52. package/dist/interface/assets/CuiLightControl-CvfWo8mY.js +1 -0
  53. package/dist/interface/assets/{CuiList-DrYuFhK4.js → CuiList-CajC6jpE.js} +1 -1
  54. package/dist/interface/assets/CuiPluginCard-CQS8C87-.js +2 -0
  55. package/dist/interface/assets/CuiPluginCategoryChip-DCNG8zl1.js +1 -0
  56. package/dist/interface/assets/{CuiPluginOAuthButton-vMuRa9Qd.js → CuiPluginOAuthButton-CvZrOWr8.js} +2 -2
  57. package/dist/interface/assets/{CuiPluginTrustBadge-2wsPzYZA.js → CuiPluginTrustBadge-Bc1dQgX8.js} +1 -1
  58. package/dist/interface/assets/CuiQRCode-CjLcB644.js +1 -0
  59. package/dist/interface/assets/CuiRecordingsGrid-DrpErbux.js +1 -0
  60. package/dist/interface/assets/{CuiRecordingsGrid-Q4C3aeLo.css → CuiRecordingsGrid-EAumRo5E.css} +1 -1
  61. package/dist/interface/assets/CuiSchema-BYYeh269.css +1 -0
  62. package/dist/interface/assets/CuiSchema-DG8peseE.js +1 -0
  63. package/dist/interface/assets/{CuiTopNavbarItem-CSdvkKHR.js → CuiTopNavbarItem-Dq3Esl1S.js} +1 -1
  64. package/dist/interface/assets/Faces-D0yRgOMj.js +1 -0
  65. package/dist/interface/assets/FirstSteps-BW8AVmd4.js +1 -0
  66. package/dist/interface/assets/{Instances-Cw7A4I7E.js → Instances-BgXJvs0_.js} +1 -1
  67. package/dist/interface/assets/{Logs-4Mgj7YWu.js → Logs-DH3rWnlV.js} +2 -2
  68. package/dist/interface/assets/Menu-D9FVgjwT.js +1 -0
  69. package/dist/interface/assets/Metrics-7bYWyGWU.js +9 -0
  70. package/dist/interface/assets/Metrics-DX59XXDc.css +1 -0
  71. package/dist/interface/assets/{OAuthDeviceFlow-DGikOkgK.js → OAuthDeviceFlow-BS0FQbZh.js} +1 -1
  72. package/dist/interface/assets/Plugin-ZwkTp6C4.js +1 -0
  73. package/dist/interface/assets/{PluginConsole-ZZvJlgIh.js → PluginConsole-Dt39TWaO.js} +2 -2
  74. package/dist/interface/assets/{PluginDetail-B9NgbzkJ.js → PluginDetail-D7ZtOG9Y.js} +2 -2
  75. package/dist/interface/assets/{PluginSchema-Cx_LInpM.js → PluginSchema-8T7EBq-Z.js} +1 -1
  76. package/dist/interface/assets/PluginSearch-DN3o0wfs.js +1 -0
  77. package/dist/interface/assets/PluginSearch-eRDCvu_8.js +2 -0
  78. package/dist/interface/assets/{PluginUpdateProgress-0nV-8LvI.js → PluginUpdateProgress-CxjpzkGa.js} +1 -1
  79. package/dist/interface/assets/Plugins-DVsMpyvr.js +1 -0
  80. package/dist/interface/assets/Recordings-BKxSEiQo.js +1 -0
  81. package/dist/interface/assets/SensorEdit-BmaMQKEa.js +1 -0
  82. package/dist/interface/assets/{SensorHistory-IgMpuLdC.js → SensorHistory-CmOAk-tD.js} +1 -1
  83. package/dist/interface/assets/Sensors-x08al7Je.js +2 -0
  84. package/dist/interface/assets/Settings-Cp1lzG1i.js +1 -0
  85. package/dist/interface/assets/SettingsAccount-BwCNddkW.js +1 -0
  86. package/dist/interface/assets/SettingsAccount-DC9AxqHe.js +1 -0
  87. package/dist/interface/assets/SettingsAppearance-CMzzDZCX.js +1 -0
  88. package/dist/interface/assets/{SettingsAppearance-C8EEBVZN.js → SettingsAppearance-Dr8hJloI.js} +1 -1
  89. package/dist/interface/assets/SettingsBackup-Dc8pDM3Y.js +1 -0
  90. package/dist/interface/assets/{SettingsList-MXYSQMZ3.js → SettingsList-3peF3gFZ.js} +1 -1
  91. package/dist/interface/assets/SettingsMqtt-k-cHaOoA.js +1 -0
  92. package/dist/interface/assets/SettingsNotifications-BJHYlSKX.js +1 -0
  93. package/dist/interface/assets/{SettingsPermissions-Dd_yfNlk.js → SettingsPermissions-CymqvfR9.js} +1 -1
  94. package/dist/interface/assets/{SettingsRecordings-85B2bQIf.js → SettingsRecordings-gWTD_djR.js} +1 -1
  95. package/dist/interface/assets/SettingsRemote-BWAhI1Gw.js +2 -0
  96. package/dist/interface/assets/SettingsRemote-CtP6Gz9C.css +1 -0
  97. package/dist/interface/assets/SettingsSystem-BGBUQvjg.js +2 -0
  98. package/dist/interface/assets/SettingsUsers-B_Ri5OrM.js +1 -0
  99. package/dist/interface/assets/Terminal-eYxvgFHE.js +4 -0
  100. package/dist/interface/assets/Updates-CweZekTf.js +2 -0
  101. package/dist/interface/assets/{VariableInput-7c7Px-DM.js → VariableInput-CM6T3pUW.js} +1 -1
  102. package/dist/interface/assets/{VariableSuggestions-CrEa2zN-.js → VariableSuggestions-BGxZxS6U.js} +1 -1
  103. package/dist/interface/assets/{VersionsHandler-BFuLGk6Y.js → VersionsHandler-BWIiBPPa.js} +2 -2
  104. package/dist/interface/assets/{VirtualSensorCreate-uumfOHgB.js → VirtualSensorCreate-CFQC2-9D.js} +1 -1
  105. package/dist/interface/assets/Workers-B-sp43t3.js +2 -0
  106. package/dist/interface/assets/{Workers-7hKMBU4w.css → Workers-DH59zE1C.css} +1 -1
  107. package/dist/interface/assets/{api-DLVsQBAO.js → api-CG0j1twy.js} +1 -1
  108. package/dist/interface/assets/api-CVOj5SYs.css +2 -0
  109. package/dist/interface/assets/api-C_bOaXk2.js +2 -0
  110. package/dist/interface/assets/{auth-uQlIia9h.js → auth-BPSOBTDX.js} +1 -1
  111. package/dist/interface/assets/{automations-ChG1qcO7.js → automations-BrtwUIUz.js} +1 -1
  112. package/dist/interface/assets/brain-7IVdhMyV.js +1 -0
  113. package/dist/interface/assets/{cameras.schema-Cv7cjMz3.js → cameras.schema-CoIOnGzE.js} +1 -1
  114. package/dist/interface/assets/cloudHandoff-BiFzJEYx.js +1 -0
  115. package/dist/interface/assets/{config-B1aWtF-N.js → config-BjldP5A6.js} +1 -1
  116. package/dist/interface/assets/{flowSchema-CNnBxcwy.js → flowSchema-B_Otn6RU.js} +1 -1
  117. package/dist/interface/assets/{intercomService-DJHBix4X.js → intercomService-BpTOoSQi.js} +1 -1
  118. package/dist/interface/assets/lan-BP0NuDr7.js +1 -0
  119. package/dist/interface/assets/main-BG-VCb--.js +3 -0
  120. package/dist/interface/assets/main-CFQGq2Yn.css +1 -0
  121. package/dist/interface/assets/{mqtt-Cyhv8shC.js → mqtt-DSPPQwxJ.js} +1 -1
  122. package/dist/interface/assets/{notifications-DniREnpL.js → notifications-DEEM2oP2.js} +1 -1
  123. package/dist/interface/assets/{plugins-5gXj0utE.js → plugins-DBBFdZKH.js} +1 -1
  124. package/dist/interface/assets/refresh-Ds2EayhZ.js +1 -0
  125. package/dist/interface/assets/sensorIcons-CP5EQZ60.js +1 -0
  126. package/dist/interface/assets/{sensors-Bc02Z1G9.js → sensors-CKTPsYYH.js} +1 -1
  127. package/dist/interface/assets/{server-D2KGja0u.js → server-B_MuqJ5z.js} +1 -1
  128. package/dist/interface/assets/{theme-vkX_uBgJ.js → theme-FztlStyg.js} +1 -1
  129. package/dist/interface/assets/types-BHAzjgi3.js +1 -0
  130. package/dist/interface/assets/types-G1_espGV.js +1 -0
  131. package/dist/interface/assets/{types-fCQkAGfW.js → types-bCt9IrFD.js} +1 -1
  132. package/dist/interface/assets/updates-CVapvGAI.js +1 -0
  133. package/dist/interface/assets/{useAppVersion-9zH_FspP.js → useAppVersion-SrF7tGCe.js} +1 -1
  134. package/dist/interface/assets/useBackupRestore-Tc23Huvm.js +1 -0
  135. package/dist/interface/assets/{useCameraOptions-BIaDXHeq.js → useCameraOptions-zsIqf0i9.js} +1 -1
  136. package/dist/interface/assets/{useLogsSocket-BLV7dWSG.js → useLogsSocket-DSltWBh4.js} +1 -1
  137. package/dist/interface/assets/{useNavLayout-wq-6JyWK.js → useNavLayout-BhlxDHV7.js} +1 -1
  138. package/dist/interface/assets/{usePermissions-BiBlyeQl.js → usePermissions-4HqGU99G.js} +1 -1
  139. package/dist/interface/assets/{usePluginUpdates-BZJSqnSF.js → usePluginUpdates-CKH0Hb87.js} +1 -1
  140. package/dist/interface/assets/{usePushRegistration-CGFiyCuv.js → usePushRegistration-DAE3goyn.js} +1 -1
  141. package/dist/interface/assets/{useServerRestart-CjPAgzoX.js → useServerRestart-DoLrF47t.js} +1 -1
  142. package/dist/interface/assets/{useStreamStatus-BFCF1B3T.js → useStreamStatus-B3QIWjDE.js} +1 -1
  143. package/dist/interface/assets/useTabSwipe-BYFLQ9EY.js +1 -0
  144. package/dist/interface/assets/{useUpdateChannel-F4WpbMJR.js → useUpdateChannel-DWEw-ZyB.js} +1 -1
  145. package/dist/interface/assets/useUpdatesSocket-CoxkS7Hr.js +1 -0
  146. package/dist/interface/assets/{useUserOptions-GbSC7_qt.js → useUserOptions-DCDeuz2p.js} +1 -1
  147. package/dist/interface/assets/vendor-cameraui-4iwjOYwz.js +2326 -0
  148. package/dist/interface/assets/vendor-core-gJradu21.js +1 -0
  149. package/dist/interface/assets/{vendor-primevue-b9CB9Uh0.js → vendor-primevue-DMAuiM9b.js} +72 -72
  150. package/dist/interface/index.html +31 -27
  151. package/dist/interface/sw.js +1 -1
  152. package/dist/server/package.json +9 -8
  153. package/dist/server/requirements.txt +1 -1
  154. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/__init__.py +4 -0
  155. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/__init__.py +4 -0
  156. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/config.py +6 -0
  157. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/detection.py +3 -3
  158. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/enums.py +6 -0
  159. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/plugin/interfaces.py +24 -0
  160. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/detection.py +3 -3
  161. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/object.py +6 -1
  162. package/dist/server/src/api/controllers/frameWorkers.controller.d.ts +2 -2
  163. package/dist/server/src/api/controllers/frameWorkers.controller.js +26 -49
  164. package/dist/server/src/api/controllers/frameWorkers.controller.js.map +1 -1
  165. package/dist/server/src/api/controllers/remote.controller.d.ts +1 -0
  166. package/dist/server/src/api/controllers/remote.controller.js +13 -12
  167. package/dist/server/src/api/controllers/remote.controller.js.map +1 -1
  168. package/dist/server/src/api/controllers/server.controller.js +5 -2
  169. package/dist/server/src/api/controllers/server.controller.js.map +1 -1
  170. package/dist/server/src/api/controllers/updates.controller.d.ts +8 -0
  171. package/dist/server/src/api/controllers/updates.controller.js +40 -0
  172. package/dist/server/src/api/controllers/updates.controller.js.map +1 -0
  173. package/dist/server/src/api/controllers/workers.controller.js +8 -2
  174. package/dist/server/src/api/controllers/workers.controller.js.map +1 -1
  175. package/dist/server/src/api/database/index.d.ts +1 -1
  176. package/dist/server/src/api/database/index.js +1 -1
  177. package/dist/server/src/api/database/migrations/v2-1-11.js.map +1 -1
  178. package/dist/server/src/api/database/migrations/v2-1-13.d.ts +3 -0
  179. package/dist/server/src/api/database/migrations/v2-1-13.js +51 -0
  180. package/dist/server/src/api/database/migrations/v2-1-13.js.map +1 -0
  181. package/dist/server/src/api/database/migrations/v2-1-14.d.ts +3 -0
  182. package/dist/server/src/api/database/migrations/v2-1-14.js +87 -0
  183. package/dist/server/src/api/database/migrations/v2-1-14.js.map +1 -0
  184. package/dist/server/src/api/routes/index.js +3 -1
  185. package/dist/server/src/api/routes/index.js.map +1 -1
  186. package/dist/server/src/api/routes/updates.routes.d.ts +2 -0
  187. package/dist/server/src/api/routes/updates.routes.js +47 -0
  188. package/dist/server/src/api/routes/updates.routes.js.map +1 -0
  189. package/dist/server/src/api/routes/workers.routes.js +3 -2
  190. package/dist/server/src/api/routes/workers.routes.js.map +1 -1
  191. package/dist/server/src/api/schemas/cameras.schema.d.ts +101 -51
  192. package/dist/server/src/api/schemas/cameras.schema.js +18 -2
  193. package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
  194. package/dist/server/src/api/schemas/workers.schema.d.ts +3 -0
  195. package/dist/server/src/api/schemas/workers.schema.js +5 -0
  196. package/dist/server/src/api/schemas/workers.schema.js.map +1 -1
  197. package/dist/server/src/api/services/plugins.service.d.ts +4 -1
  198. package/dist/server/src/api/services/plugins.service.js +23 -4
  199. package/dist/server/src/api/services/plugins.service.js.map +1 -1
  200. package/dist/server/src/api/services/server.service.d.ts +3 -1
  201. package/dist/server/src/api/services/server.service.js +6 -1
  202. package/dist/server/src/api/services/server.service.js.map +1 -1
  203. package/dist/server/src/api/services/updates.service.d.ts +108 -0
  204. package/dist/server/src/api/services/updates.service.js +476 -0
  205. package/dist/server/src/api/services/updates.service.js.map +1 -0
  206. package/dist/server/src/api/types/index.d.ts +6 -1
  207. package/dist/server/src/api/websocket/index.js +2 -0
  208. package/dist/server/src/api/websocket/index.js.map +1 -1
  209. package/dist/server/src/api/websocket/nsp/metrics.d.ts +2 -0
  210. package/dist/server/src/api/websocket/nsp/metrics.js +36 -4
  211. package/dist/server/src/api/websocket/nsp/metrics.js.map +1 -1
  212. package/dist/server/src/api/websocket/nsp/server.js +43 -7
  213. package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
  214. package/dist/server/src/api/websocket/types.d.ts +3 -0
  215. package/dist/server/src/camera/decoder/debug/detection-record.d.ts +6 -0
  216. package/dist/server/src/camera/decoder/debug/detection-record.js +21 -0
  217. package/dist/server/src/camera/decoder/debug/detection-record.js.map +1 -1
  218. package/dist/server/src/camera/decoder/detection-coordinator.d.ts +19 -1
  219. package/dist/server/src/camera/decoder/detection-coordinator.js +306 -84
  220. package/dist/server/src/camera/decoder/detection-coordinator.js.map +1 -1
  221. package/dist/server/src/camera/decoder/detection-pipeline.d.ts +3 -1
  222. package/dist/server/src/camera/decoder/detection-pipeline.js +12 -4
  223. package/dist/server/src/camera/decoder/detection-pipeline.js.map +1 -1
  224. package/dist/server/src/camera/decoder/detection-window.d.ts +17 -0
  225. package/dist/server/src/camera/decoder/detection-window.js +188 -0
  226. package/dist/server/src/camera/decoder/detection-window.js.map +1 -0
  227. package/dist/server/src/camera/decoder/event-manager.d.ts +1 -0
  228. package/dist/server/src/camera/decoder/event-manager.js +12 -0
  229. package/dist/server/src/camera/decoder/event-manager.js.map +1 -1
  230. package/dist/server/src/camera/decoder/frame-scaler.d.ts +12 -0
  231. package/dist/server/src/camera/decoder/frame-scaler.js +38 -7
  232. package/dist/server/src/camera/decoder/frame-scaler.js.map +1 -1
  233. package/dist/server/src/camera/decoder/motion-localizer.d.ts +16 -0
  234. package/dist/server/src/camera/decoder/motion-localizer.js +49 -0
  235. package/dist/server/src/camera/decoder/motion-localizer.js.map +1 -0
  236. package/dist/server/src/camera/decoder/perf-tracker.d.ts +2 -0
  237. package/dist/server/src/camera/decoder/perf-tracker.js +4 -0
  238. package/dist/server/src/camera/decoder/perf-tracker.js.map +1 -1
  239. package/dist/server/src/camera/decoder/plugin-registry.js +1 -6
  240. package/dist/server/src/camera/decoder/plugin-registry.js.map +1 -1
  241. package/dist/server/src/camera/decoder/sources/analysis-source.d.ts +1 -0
  242. package/dist/server/src/camera/decoder/sources/buffered-source.d.ts +1 -0
  243. package/dist/server/src/camera/decoder/sources/buffered-source.js +38 -0
  244. package/dist/server/src/camera/decoder/sources/buffered-source.js.map +1 -1
  245. package/dist/server/src/camera/decoder/types.d.ts +9 -8
  246. package/dist/server/src/camera/decoder/types.js.map +1 -1
  247. package/dist/server/src/main.js +22 -2
  248. package/dist/server/src/main.js.map +1 -1
  249. package/dist/server/src/manager/types.d.ts +1 -0
  250. package/dist/server/src/manager/types.js +2 -0
  251. package/dist/server/src/manager/types.js.map +1 -1
  252. package/dist/server/src/mqtt/ha-discovery.js +0 -1
  253. package/dist/server/src/mqtt/ha-discovery.js.map +1 -1
  254. package/dist/server/src/plugins/index.js +0 -2
  255. package/dist/server/src/plugins/index.js.map +1 -1
  256. package/dist/server/src/remote/types.d.ts +1 -2
  257. package/dist/server/src/rpc/leafConnection.d.ts +12 -0
  258. package/dist/server/src/rpc/leafConnection.js +60 -0
  259. package/dist/server/src/rpc/leafConnection.js.map +1 -0
  260. package/dist/server/src/rpc/server.d.ts +2 -0
  261. package/dist/server/src/rpc/server.js +7 -1
  262. package/dist/server/src/rpc/server.js.map +1 -1
  263. package/dist/server/src/services/config/index.js +2 -0
  264. package/dist/server/src/services/config/index.js.map +1 -1
  265. package/dist/server/src/types.d.ts +10 -1
  266. package/dist/server/src/utils/ipc.d.ts +9 -0
  267. package/dist/server/src/utils/ipc.js +31 -5
  268. package/dist/server/src/utils/ipc.js.map +1 -1
  269. package/dist/server/src/utils/system-info.d.ts +9 -0
  270. package/dist/server/src/utils/system-info.js +48 -0
  271. package/dist/server/src/utils/system-info.js.map +1 -0
  272. package/dist/server/src/workers/agent.d.ts +3 -0
  273. package/dist/server/src/workers/agent.js +35 -7
  274. package/dist/server/src/workers/agent.js.map +1 -1
  275. package/dist/server/src/workers/capabilities/frame-decoding.d.ts +2 -2
  276. package/dist/server/src/workers/capabilities/frame-decoding.js +5 -5
  277. package/dist/server/src/workers/capabilities/frame-decoding.js.map +1 -1
  278. package/dist/server/src/workers/capabilities/handler.d.ts +2 -2
  279. package/dist/server/src/workers/capabilities/plugin-host.d.ts +2 -2
  280. package/dist/server/src/workers/capabilities/plugin-host.js +6 -6
  281. package/dist/server/src/workers/capabilities/plugin-host.js.map +1 -1
  282. package/dist/server/src/workers/manager.d.ts +9 -1
  283. package/dist/server/src/workers/manager.js +24 -0
  284. package/dist/server/src/workers/manager.js.map +1 -1
  285. package/dist/server/src/workers/types.d.ts +14 -0
  286. package/dist/server/src/workers/types.js.map +1 -1
  287. package/package.json +9 -8
  288. package/dist/interface/assets/Automation-DA89AEEI.js +0 -3
  289. package/dist/interface/assets/AutomationImport-hYr3Lp8F.js +0 -2
  290. package/dist/interface/assets/Automations-B6WFJyxS.js +0 -2
  291. package/dist/interface/assets/Camera-C5gF7yVN.css +0 -1
  292. package/dist/interface/assets/Camera-DmsSKvpY.js +0 -2
  293. package/dist/interface/assets/Cameras-KOOphtlx.js +0 -652
  294. package/dist/interface/assets/Camview-BPQIinyP.js +0 -1
  295. package/dist/interface/assets/ConfigTriggerDetection-iThbCKOO.js +0 -1
  296. package/dist/interface/assets/ConfigTriggerGeofence-BeKwVWvS.js +0 -1
  297. package/dist/interface/assets/Console-N_2Vornl.js +0 -1
  298. package/dist/interface/assets/CuiAutomationRunsDialog-Cp6Apki9.js +0 -1
  299. package/dist/interface/assets/CuiBottomSheet-CIR_fmSk.js +0 -1
  300. package/dist/interface/assets/CuiCameraPipCard-DP6X45oq.js +0 -1
  301. package/dist/interface/assets/CuiCameraRecordings-CRjXsdO4.js +0 -1
  302. package/dist/interface/assets/CuiCameraTable-D5ZqTLVt.js +0 -1
  303. package/dist/interface/assets/CuiDataTable-wA_LbHH8.js +0 -1
  304. package/dist/interface/assets/CuiLightControl-CB_Tp4tI.js +0 -1
  305. package/dist/interface/assets/CuiPluginCard-DEuNt5ni.js +0 -2
  306. package/dist/interface/assets/CuiPluginCategoryChip-BbkbkYj6.js +0 -1
  307. package/dist/interface/assets/CuiQRCode-Dh9EcaVc.js +0 -1
  308. package/dist/interface/assets/CuiRecordingsGrid-HjZEiSyp.js +0 -1
  309. package/dist/interface/assets/CuiSchema--yMBD996.css +0 -1
  310. package/dist/interface/assets/CuiSchema-CSFoI6cg.js +0 -1
  311. package/dist/interface/assets/Faces-CPZ5RA68.js +0 -1
  312. package/dist/interface/assets/FirstSteps-CaF8STWH.js +0 -1
  313. package/dist/interface/assets/Menu-T-54ZSSD.js +0 -1
  314. package/dist/interface/assets/Metrics-CQGKmHev.css +0 -1
  315. package/dist/interface/assets/Metrics-Dgm5qpzh.js +0 -6
  316. package/dist/interface/assets/Plugin-YxqbMbUc.js +0 -1
  317. package/dist/interface/assets/PluginSearch-5AgSWlmi.js +0 -1
  318. package/dist/interface/assets/PluginSearch-P274oMvy.js +0 -2
  319. package/dist/interface/assets/Plugins-Bvru3q2P.js +0 -1
  320. package/dist/interface/assets/Recordings-BWGWC6Kq.js +0 -1
  321. package/dist/interface/assets/SensorEdit-gEw6HHZs.js +0 -1
  322. package/dist/interface/assets/Sensors-DukA8Xqx.js +0 -2
  323. package/dist/interface/assets/Settings-DWpeWPJh.js +0 -1
  324. package/dist/interface/assets/SettingsAccount-C1OpCtob.js +0 -1
  325. package/dist/interface/assets/SettingsAccount-CrLcqzSn.js +0 -1
  326. package/dist/interface/assets/SettingsAppearance-AhUjml5E.js +0 -1
  327. package/dist/interface/assets/SettingsBackup-BJupyZCJ.js +0 -1
  328. package/dist/interface/assets/SettingsMqtt-F3pV1Bvo.js +0 -1
  329. package/dist/interface/assets/SettingsNotifications-BmaAccX9.js +0 -1
  330. package/dist/interface/assets/SettingsRemote-C_ikoXW7.js +0 -2
  331. package/dist/interface/assets/SettingsRemote-DzrfpHjw.css +0 -1
  332. package/dist/interface/assets/SettingsSystem-Ct3qms5O.js +0 -2
  333. package/dist/interface/assets/SettingsUsers-D36vZ8Zf.js +0 -1
  334. package/dist/interface/assets/Terminal-ltPiB9CB.js +0 -4
  335. package/dist/interface/assets/Workers-Bowrc1AF.js +0 -2
  336. package/dist/interface/assets/api-daCggwpn.css +0 -2
  337. package/dist/interface/assets/api-py5HP4SF.js +0 -2
  338. package/dist/interface/assets/cloudHandoff-CL4sircA.js +0 -1
  339. package/dist/interface/assets/deployed-code-update-xHE-blDN.js +0 -1
  340. package/dist/interface/assets/main-CLvcwWW7.js +0 -3
  341. package/dist/interface/assets/main-mJXdi_gx.css +0 -1
  342. package/dist/interface/assets/round-restart-alt-D3jT5i_T.js +0 -1
  343. package/dist/interface/assets/round-restart-alt-DdIfy7mU.css +0 -1
  344. package/dist/interface/assets/sensorIcons-BXauWntX.js +0 -1
  345. package/dist/interface/assets/types-CrvnT4-i.js +0 -1
  346. package/dist/interface/assets/types-D6IQ6ssc.js +0 -1
  347. package/dist/interface/assets/useBackupRestore-Dbf24OqP.js +0 -1
  348. package/dist/interface/assets/vendor-cameraui-0EJfDg8o.js +0 -2326
  349. package/dist/interface/assets/vendor-core-RnPzfwaA.js +0 -1
@@ -19,11 +19,13 @@ import { AudioDetectionLoop } from './audio-loop.js';
19
19
  import { CascadeManager } from './cascade-manager.js';
20
20
  import { detectionRecord } from './debug/detection-record.js';
21
21
  import { DetectionPipeline } from './detection-pipeline.js';
22
+ import { clusterBoxes, DetectionWindow, mergeWindowDetections, MOTION_PAD, planWindowsOnce, TRACK_PAD } from './detection-window.js';
22
23
  import { DwellManager } from './dwell-manager.js';
23
24
  import { DetectionEventManager, MOMENT_RANK_ATTRIBUTE, MOMENT_RANK_OBJECT } from './event-manager.js';
24
25
  import { EventThumbnailer } from './event-thumbnailer.js';
25
26
  import { FrameScaler } from './frame-scaler.js';
26
27
  import { directionBetween, directionOf, MOMENT_FORMATS, MOMENT_QUALITY, momentWindow, unionBox } from './moment-crop.js';
28
+ import { MotionLocalizer } from './motion-localizer.js';
27
29
  import { PerfTracker } from './perf-tracker.js';
28
30
  import { normalizePlateText } from './plate-vote.js';
29
31
  import { isVideoInputSpec, modelIdentity, PluginRegistry } from './plugin-registry.js';
@@ -74,6 +76,13 @@ let DetectionCoordinator = class DetectionCoordinator {
74
76
  perf = new PerfTracker();
75
77
  benchmarkRunning = false;
76
78
  hqUpgrade;
79
+ localizer;
80
+ lastLocalizeAt = 0;
81
+ anchorBoxes = [];
82
+ trackAnchorBoxes = [];
83
+ farewellBoxes = [];
84
+ lastTrackedById = new Map();
85
+ detectionWindow = new DetectionWindow();
77
86
  mainStreamActive = false;
78
87
  idleSince = 0;
79
88
  lastMotionAt = 0;
@@ -115,6 +124,7 @@ let DetectionCoordinator = class DetectionCoordinator {
115
124
  if (config.streamHot)
116
125
  this.frameSource.start();
117
126
  this.frameScaler = new FrameScaler(null, logger, this.privacy);
127
+ this.localizer = new MotionLocalizer();
118
128
  this.pipeline = new DetectionPipeline(this.pipelineZones(), config.detectionSettings);
119
129
  this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, this.perf, logger);
120
130
  if (config.zones.lines.length > 0) {
@@ -167,6 +177,7 @@ let DetectionCoordinator = class DetectionCoordinator {
167
177
  this.cascadeUnsubscribe = this.cascade.onChange((event) => {
168
178
  if (event.type === 'activated') {
169
179
  this.startAdHocVideoLoopIfNeeded();
180
+ this.seedLocalizerReference();
170
181
  }
171
182
  else {
172
183
  this.stopAdHocVideoLoopIfIdle();
@@ -194,6 +205,7 @@ let DetectionCoordinator = class DetectionCoordinator {
194
205
  if (!this.dwell.hasActive() && this.worldSpans.size === 0 && !this.eventManager.hasActiveSegment()) {
195
206
  this.eventManager.forceEndActiveEvent();
196
207
  }
208
+ this.stopAdHocVideoLoopIfIdle();
197
209
  }
198
210
  });
199
211
  }
@@ -390,6 +402,9 @@ let DetectionCoordinator = class DetectionCoordinator {
390
402
  this.ptzAutotracker.handlePresenceDetections(externalDetections);
391
403
  if (this.ptzAutotracker.suppressionActive)
392
404
  return;
405
+ detectionRecord.tick({
406
+ externalObject: { detected: filtered.detected === true, count: (filtered.detections ?? []).length },
407
+ });
393
408
  this.ingestDetectionResult(SensorType.Object, sensorId, filtered);
394
409
  this.eventManager.processResults(this.buildSnapshot());
395
410
  // re-shoot the event thumbnail: smart-camera reports arrive after the
@@ -403,8 +418,10 @@ let DetectionCoordinator = class DetectionCoordinator {
403
418
  if (filtered.detected !== true)
404
419
  return;
405
420
  // assist alone is enough: its boxes upgrade zones, bboxes and thumbnails
406
- // even when no face/plate/clip secondary consumes them
407
- if (!this.plugins.hasFrameBasedSecondary() && !this.plugins.hasEligibleObjectAssist())
421
+ // even when no face/plate/clip secondary consumes them; without either,
422
+ // the embedded diff's anchors can still localize a moment crop
423
+ const hasAnchors = this.anchorBoxes.length > 0 || this.farewellBoxes.length > 0;
424
+ if (!this.plugins.hasFrameBasedSecondary() && !this.plugins.hasEligibleObjectAssist() && !hasAnchors)
408
425
  return;
409
426
  if (this.processingExternalSecondary)
410
427
  return; // previous RPC still running
@@ -416,42 +433,60 @@ let DetectionCoordinator = class DetectionCoordinator {
416
433
  return;
417
434
  try {
418
435
  const results = { timestamp: Date.now() };
419
- const objects = await this.runObjectAssist(handle.frame, rawExternal);
420
- this.ingestAssistedObjects(sensorId, objects);
421
- if (objects.assisted) {
422
- // real boxes: crop the object like the frame pipeline, so face and
423
- // plate secondaries run on the subject instead of the whole scene
424
- // (processExternal only adapts the shape, external boxes get no real tracking)
425
- const objectDetections = this.pipeline.processExternal(objects.detections);
426
- await this.runSecondariesAndThumbnails({ frame: handle.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
427
- this.ingestResultsForAllSecondaries(results);
428
- const snapshot = this.buildSnapshot();
429
- if (results.thumbnails && results.thumbnails.length > 0) {
430
- snapshot.thumbnails = results.thumbnails;
431
- }
432
- this.eventManager.processResults(snapshot);
433
- }
434
- else {
435
- const sceneJpeg = await this.secondaries.detectFullFrame(handle.frame, results);
436
- try {
437
- results.thumbnails = await this.secondaries.generateThumbnails(handle.frame, this.frameScaler, results);
438
- }
439
- catch (error) {
440
- this.logger.error('Thumbnail generation error:', error);
441
- }
442
- this.ingestResultsForAllSecondaries(results);
443
- const snapshot = this.buildSnapshot();
444
- if (results.thumbnails && results.thumbnails.length > 0) {
445
- snapshot.thumbnails = results.thumbnails;
436
+ // anchors may have switched analysis to the main stream: the assist
437
+ // windows and secondary crops should see those native pixels
438
+ const analysis = await this.acquireAnalysisFrame(handle.frame);
439
+ try {
440
+ const objects = await this.runObjectAssist(analysis.frame, rawExternal, analysis.scaler);
441
+ this.ingestAssistedObjects(sensorId, objects);
442
+ if (objects.assisted) {
443
+ // real boxes: crop the object like the frame pipeline, so face and
444
+ // plate secondaries run on the subject instead of the whole scene
445
+ // (processExternal only adapts the shape, external boxes get no real tracking)
446
+ const objectDetections = this.pipeline.processExternal(objects.detections);
447
+ detectionRecord.tick({ assistProcessed: { count: objectDetections.length, bufferedObjects: this.currentDetectionState.object?.detections?.length ?? 0 } });
448
+ await this.runSecondariesAndThumbnails(analysis, objectDetections, results);
449
+ await this.captureExternalMoment(objectDetections, analysis, results.timestamp);
450
+ this.ingestResultsForAllSecondaries(results);
451
+ const snapshot = this.buildSnapshot();
452
+ detectionRecord.tick({ assistSnapshot: { objects: snapshot.objects.length } });
453
+ if (results.thumbnails && results.thumbnails.length > 0) {
454
+ snapshot.thumbnails = results.thumbnails;
455
+ }
456
+ this.eventManager.processResults(snapshot);
446
457
  }
447
- if (sceneJpeg)
448
- snapshot.eventThumbnail = sceneJpeg;
449
- const wantsEventThumb = this.eventManager.needsThumbnail() || this.snapshotWillStartEvent(snapshot);
450
- this.eventManager.processResults(snapshot);
451
- if (wantsEventThumb) {
452
- this.thumbnailer.upgradeEventThumbnailAsync();
458
+ else {
459
+ // no assist boxes: the diff's motion clusters are the only
460
+ // localization, still better than a full-scene card
461
+ const anchorDetections = this.anchorMomentDetections(rawExternal);
462
+ if (anchorDetections.length > 0) {
463
+ await this.captureExternalMoment(anchorDetections, analysis, results.timestamp);
464
+ }
465
+ const sceneJpeg = await this.secondaries.detectFullFrame(analysis.frame, results);
466
+ try {
467
+ results.thumbnails = await this.secondaries.generateThumbnails(analysis.frame, analysis.scaler, results);
468
+ }
469
+ catch (error) {
470
+ this.logger.error('Thumbnail generation error:', error);
471
+ }
472
+ this.ingestResultsForAllSecondaries(results);
473
+ const snapshot = this.buildSnapshot();
474
+ if (results.thumbnails && results.thumbnails.length > 0) {
475
+ snapshot.thumbnails = results.thumbnails;
476
+ }
477
+ if (sceneJpeg)
478
+ snapshot.eventThumbnail = sceneJpeg;
479
+ const wantsEventThumb = this.eventManager.needsThumbnail() || this.snapshotWillStartEvent(snapshot);
480
+ this.eventManager.processResults(snapshot);
481
+ if (wantsEventThumb) {
482
+ this.thumbnailer.upgradeEventThumbnailAsync();
483
+ }
453
484
  }
454
485
  }
486
+ finally {
487
+ if (analysis.frame !== handle.frame)
488
+ analysis.frame[Symbol.dispose]?.();
489
+ }
455
490
  }
456
491
  finally {
457
492
  await handle[Symbol.asyncDispose]();
@@ -511,18 +546,28 @@ let DetectionCoordinator = class DetectionCoordinator {
511
546
  hasPlugin(sensorType) {
512
547
  return this.plugins.has(sensorType);
513
548
  }
514
- externalObjectSpanActive() {
515
- // the registry holds frame-based providers only; a smart-camera object
516
- // sensor (Reolink AI, ONVIF) lives in the feeding set, and its dwell is
517
- // what keeps the span open between its report edges
518
- if (this.plugins.get(SensorType.Object)?.requiresFrames)
519
- return false;
520
- for (const [sensorId, sensorType] of this.feedingSensors) {
521
- if (sensorType === SensorType.Object && this.dwell.isActive(sensorId))
549
+ get windowAnchorsWanted() {
550
+ return this.plugins.get(SensorType.Object)?.requiresFrames === true || this.plugins.hasEligibleObjectAssist();
551
+ }
552
+ get localizerWanted() {
553
+ // the registry only holds frame-based providers; a smart-camera object
554
+ // sensor (Reolink AI, ONVIF) feeds externally and needs the anchors just
555
+ // as much — for assist zoom windows and for framing its moment pictures
556
+ if (this.plugins.has(SensorType.Object) || this.plugins.hasEligibleObjectAssist())
557
+ return true;
558
+ for (const sensorType of this.feedingSensors.values()) {
559
+ if (sensorType === SensorType.Object)
522
560
  return true;
523
561
  }
524
562
  return false;
525
563
  }
564
+ get windowWantsMainStream() {
565
+ if (!this.cascade.isActive)
566
+ return false;
567
+ if (this.anchorBoxes.length === 0 && this.trackAnchorBoxes.length === 0)
568
+ return false;
569
+ return this.windowAnchorsWanted;
570
+ }
526
571
  get cascadeEnabled() {
527
572
  return this.config.detectionSettings.cascadeDetection !== false;
528
573
  }
@@ -539,7 +584,22 @@ let DetectionCoordinator = class DetectionCoordinator {
539
584
  const [w, h] = this.config.interfaceSettings.aspectRatio.split(':').map(Number);
540
585
  return w / h;
541
586
  }
587
+ externalObjectSpanActive() {
588
+ // the registry holds frame-based providers only; a smart-camera object
589
+ // sensor (Reolink AI, ONVIF) lives in the feeding set, and its dwell is
590
+ // what keeps the span open between its report edges
591
+ if (this.plugins.get(SensorType.Object)?.requiresFrames)
592
+ return false;
593
+ for (const [sensorId, sensorType] of this.feedingSensors) {
594
+ if (sensorType === SensorType.Object && this.dwell.isActive(sensorId))
595
+ return true;
596
+ }
597
+ return false;
598
+ }
542
599
  handleCascadeDeactivated() {
600
+ this.anchorBoxes = [];
601
+ this.farewellBoxes = [];
602
+ this.detectionWindow.reset();
543
603
  // clear cascade-gated buffer slots, keep motion/audio
544
604
  const cs = this.currentDetectionState;
545
605
  cs.object = undefined;
@@ -965,18 +1025,26 @@ let DetectionCoordinator = class DetectionCoordinator {
965
1025
  }
966
1026
  }
967
1027
  startAdHocVideoLoopIfNeeded() {
968
- if (!this.loopRunning && this.plugins.needsAdHocLoop()) {
1028
+ // localizerWanted covers what the registry cannot see: an object assist or
1029
+ // an external object sensor needs the loop for the localizer's anchors
1030
+ if (!this.loopRunning && (this.plugins.needsAdHocLoop() || this.localizerWanted)) {
969
1031
  this.adHocVideoLoop = true;
970
1032
  this.logger.debug('Starting ad-hoc video loop for sensor cascade trigger');
971
1033
  this.startVideoLoop();
972
1034
  }
973
1035
  }
974
1036
  stopAdHocVideoLoopIfIdle() {
975
- if (this.adHocVideoLoop && !this.plugins.shouldVideoBeActive()) {
976
- this.adHocVideoLoop = false;
977
- this.logger.debug('Stopping ad-hoc video loop — cascade ended');
978
- this.stopVideoLoop();
979
- }
1037
+ if (!this.adHocVideoLoop || this.plugins.shouldVideoBeActive())
1038
+ return;
1039
+ // the localizer and assist still need frames while the event runs;
1040
+ // stopping on the cascade edge alone churned the main-stream decoder
1041
+ // open/closed mid-event, which starves the hw device (vaapi surface sync
1042
+ // failures) right when the next report arrives
1043
+ if (this.cascade.isActive || this.dwell.hasActive() || this.eventManager.hasActiveSegment())
1044
+ return;
1045
+ this.adHocVideoLoop = false;
1046
+ this.logger.debug('Stopping ad-hoc video loop — idle');
1047
+ this.stopVideoLoop();
980
1048
  }
981
1049
  async runDetectionLoop() {
982
1050
  // the source reconnects on its own, nextFrame simply waits it out
@@ -1009,6 +1077,8 @@ let DetectionCoordinator = class DetectionCoordinator {
1009
1077
  this.frameScaler.updateHardwareContext(this.frameSource.hardwareContext);
1010
1078
  this.frameScaler.clearCache();
1011
1079
  this.thumbnailer.sync(this.mainStreamAnalysisWanted);
1080
+ this.localizer.reset();
1081
+ this.detectionWindow.reset();
1012
1082
  }
1013
1083
  // motion always reads the low stream: switching its input resolution
1014
1084
  // would reset the background model on every transition
@@ -1106,7 +1176,7 @@ let DetectionCoordinator = class DetectionCoordinator {
1106
1176
  return undefined;
1107
1177
  }
1108
1178
  updateStreamState() {
1109
- if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment()) {
1179
+ if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment() || this.windowWantsMainStream) {
1110
1180
  this.idleSince = 0;
1111
1181
  if (!this.mainStreamActive && this.mainStreamAvailable) {
1112
1182
  this.mainStreamActive = true;
@@ -1141,6 +1211,7 @@ let DetectionCoordinator = class DetectionCoordinator {
1141
1211
  const t0 = Date.now();
1142
1212
  let motionDetected = false;
1143
1213
  let objectDetections = [];
1214
+ let staticDetections = [];
1144
1215
  const results = { timestamp: t0 };
1145
1216
  // while the PTZ repositions: motion and object keep running so the
1146
1217
  // background model and track ids survive the pan, but nothing below
@@ -1160,6 +1231,8 @@ let DetectionCoordinator = class DetectionCoordinator {
1160
1231
  return;
1161
1232
  if (motionFrame) {
1162
1233
  this.lastMotionAt = t0;
1234
+ if (this.localizerWanted)
1235
+ this.feedLocalizer(motionFrame, t0, ptzSuppressed);
1163
1236
  try {
1164
1237
  const motionInferStart = Date.now();
1165
1238
  const result = await motionPlugin.proxy.detectMotion(motionFrame);
@@ -1193,6 +1266,17 @@ let DetectionCoordinator = class DetectionCoordinator {
1193
1266
  // no frame-based motion plugin, the cascade is the only motion-equivalent signal
1194
1267
  motionDetected = true;
1195
1268
  results.cascadeTriggered = true;
1269
+ // the localizer scales its own gray here, nobody else needed one
1270
+ const localizeDue = t0 - this.lastLocalizeAt >= MOTION_INTERVAL_MS - TICK_SLACK_MS;
1271
+ if (localizeDue && this.localizerWanted) {
1272
+ const scaleStart = Date.now();
1273
+ const gray = await this.scaleForMotionInput(motionRawFrame);
1274
+ this.perf.scaleMs += Date.now() - scaleStart;
1275
+ if (!this.loopRunning)
1276
+ return;
1277
+ if (gray)
1278
+ this.feedLocalizer(gray, t0, ptzSuppressed);
1279
+ }
1196
1280
  }
1197
1281
  // detected is re-derived after zone filtering, the plugin's own flag is ignored
1198
1282
  if (results.motion && motionPlugin) {
@@ -1206,25 +1290,16 @@ let DetectionCoordinator = class DetectionCoordinator {
1206
1290
  const objectPlugin = this.plugins.get(SensorType.Object);
1207
1291
  const shouldDetectObjects = objectPlugin?.requiresFrames === true && (ptzSuppressed || !this.cascadeEnabled || this.cascade.isActive);
1208
1292
  if (shouldDetectObjects) {
1209
- const scaleStart = Date.now();
1210
- const objectFrame = await this.scaleForObject(analysis.frame, analysis.scaler);
1211
- this.perf.scaleMs += Date.now() - scaleStart;
1212
- if (!this.loopRunning)
1213
- return;
1214
- if (objectFrame) {
1215
- try {
1216
- const inferStart = Date.now();
1217
- const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
1218
- this.perf.objectMs += Date.now() - inferStart;
1219
- this.perf.objectCount++;
1220
- if (!this.loopRunning)
1221
- return;
1293
+ try {
1294
+ const detected = await this.runObjectDetection(objectPlugin, analysis, results.motion?.detections ?? []);
1295
+ if (!this.loopRunning)
1296
+ return;
1297
+ if (detected) {
1222
1298
  this.trackDetectionCadence();
1223
1299
  // run the pipeline even on empty frames so Norfair advances its
1224
1300
  // Kalman state; the pose delta keeps predictions stable across pans
1225
1301
  const poseDelta = this.ptzAutotracker.consumePoseDelta();
1226
1302
  const postStart = Date.now();
1227
- const detected = FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
1228
1303
  const pipelineResult = this.pipeline.process(detected, poseDelta);
1229
1304
  this.perf.postMs += Date.now() - postStart;
1230
1305
  // the tick that opens a span analysed the low stream (the switch lands
@@ -1258,25 +1333,35 @@ let DetectionCoordinator = class DetectionCoordinator {
1258
1333
  return cx > -0.1 && cx < 1.1 && cy > -0.1 && cy < 1.1;
1259
1334
  });
1260
1335
  objectDetections = visibleTracks.filter((t) => !t.trackLost);
1261
- results.object = { ...result, detections: visibleTracks };
1336
+ results.object = { detected: objectDetections.length > 0, detections: visibleTracks };
1337
+ staticDetections = pipelineResult.staticTracks;
1262
1338
  if (pipelineResult.crossings.length > 0)
1263
1339
  results.lineCrossings = pipelineResult.crossings;
1264
1340
  // incl. extrapolated tracks, otherwise a single missed detector
1265
1341
  // frame flips the autotracker into LOST/REACQUIRE churn
1266
1342
  this.ptzAutotracker.handleObjectDetections(pipelineResult.tracked);
1267
- }
1268
- catch (error) {
1269
- if (!this.loopRunning || isNoRespondersError(error))
1270
- return;
1271
- this.logger.error('Object detection error:', error);
1343
+ // live tracks anchor the next tick's window; a track that just died
1344
+ // gets one farewell look at its last position
1345
+ this.trackAnchorBoxes = objectDetections.map((t) => t.box);
1346
+ for (const id of pipelineResult.removed) {
1347
+ const last = this.lastTrackedById.get(id);
1348
+ if (last)
1349
+ this.farewellBoxes.push(last);
1350
+ }
1351
+ this.lastTrackedById = new Map(pipelineResult.tracked.filter((t) => t.trackId !== undefined).map((t) => [t.trackId, t.box]));
1272
1352
  }
1273
1353
  }
1354
+ catch (error) {
1355
+ if (!this.loopRunning || isNoRespondersError(error))
1356
+ return;
1357
+ this.logger.error('Object detection error:', error);
1358
+ }
1274
1359
  }
1275
1360
  // tracker and autotracker are fed, everything event-facing stays quiet
1276
1361
  if (ptzSuppressed) {
1277
1362
  // object boxes stay live in the UI through the move, bbox publish only
1278
1363
  if (objectPlugin && results.object) {
1279
- this.writeSensorProperties(objectPlugin.sensorId, { detections: results.object.detections });
1364
+ this.writeSensorProperties(objectPlugin.sensorId, { detections: results.object.detections, staticDetections });
1280
1365
  }
1281
1366
  // keep an already-running object dwell alive so a chase doesn't end its
1282
1367
  // own event, but never activate it from suppressed frames
@@ -1295,6 +1380,7 @@ let DetectionCoordinator = class DetectionCoordinator {
1295
1380
  this.ingestDetectionResult(SensorType.Object, objectPlugin.sensorId, {
1296
1381
  detected: objectDetections.length > 0,
1297
1382
  detections: results.object.detections,
1383
+ staticDetections,
1298
1384
  });
1299
1385
  }
1300
1386
  if (!this.loopRunning)
@@ -1493,6 +1579,41 @@ let DetectionCoordinator = class DetectionCoordinator {
1493
1579
  return;
1494
1580
  await this.captureMoment(subject, bestScore, trigger, result.tracked, analysis, at);
1495
1581
  }
1582
+ anchorMomentDetections(reported) {
1583
+ const anchors = [...this.anchorBoxes, ...this.farewellBoxes];
1584
+ if (anchors.length === 0 || reported.length === 0)
1585
+ return [];
1586
+ const subject = reported.find((d) => d.label !== 'motion') ?? reported[0];
1587
+ const clusters = clusterBoxes(anchors);
1588
+ const largest = Math.max(...clusters.map((b) => b.width * b.height));
1589
+ // specks would drag the base union across the frame
1590
+ return clusters.filter((b) => b.width * b.height >= largest * 0.2).map((b) => ({ label: subject.label, confidence: subject.confidence, box: b }));
1591
+ }
1592
+ async captureExternalMoment(detections, analysis, at) {
1593
+ let subject;
1594
+ let bestScore = 0;
1595
+ for (const detection of detections) {
1596
+ const box = detection.box;
1597
+ if (!box)
1598
+ continue;
1599
+ const score = detection.confidence * Math.sqrt(box.width * box.height);
1600
+ if (score > bestScore) {
1601
+ bestScore = score;
1602
+ subject = detection;
1603
+ }
1604
+ }
1605
+ if (!subject)
1606
+ return;
1607
+ if (!this.eventManager.wantsMoment(MOMENT_RANK_OBJECT, bestScore, 'low', at))
1608
+ return;
1609
+ const target = { subject: subject.box, base: unionBox(detections.map((d) => d.box).filter(Boolean)) };
1610
+ const rendered = await this.renderMoment(target, analysis);
1611
+ if (!rendered)
1612
+ return;
1613
+ this.eventManager.offerMoment({ strip: rendered.strip, card: rendered.card, capturedAt: at, score: bestScore, rank: MOMENT_RANK_OBJECT, stream: 'low' });
1614
+ // debugging
1615
+ await this.recordMoment(target, rendered, analysis, `${subject.label}-external`, bestScore, 'external', at);
1616
+ }
1496
1617
  async captureMoment(subject, score, trigger, tracked, analysis, at) {
1497
1618
  const box = { x: subject.x, y: subject.y, width: subject.width, height: subject.height };
1498
1619
  const moving = tracked.filter((d) => (d.trackSpeed ?? 0) >= MOMENT_MOVING_SPEED).map((d) => d.box);
@@ -1527,7 +1648,6 @@ let DetectionCoordinator = class DetectionCoordinator {
1527
1648
  // legible one frames the shot, the rest stay their own attribute tiles
1528
1649
  let best;
1529
1650
  let bestScore = -1;
1530
- let bestCard;
1531
1651
  for (const candidate of candidates) {
1532
1652
  const cut = results.thumbnails?.find((t) => t.label === candidate.label);
1533
1653
  // a subject cut off by the frame edge makes a poor card
@@ -1542,20 +1662,21 @@ let DetectionCoordinator = class DetectionCoordinator {
1542
1662
  if (score > bestScore) {
1543
1663
  bestScore = score;
1544
1664
  best = candidate;
1545
- bestCard = cut?.jpeg;
1546
1665
  }
1547
1666
  }
1548
1667
  // ask before cutting: the same face rides along on every tick of a segment
1549
1668
  // and re-encoding for a picture that loses anyway is pure cost
1550
1669
  if (!best || !this.eventManager.wantsMoment(MOMENT_RANK_ATTRIBUTE, bestScore, analysis.isMainStream ? 'main' : 'low', at))
1551
1670
  return;
1671
+ // the card frames the whole person with the face as its center, not the
1672
+ // bare face box: the zoomed face already exists as an attribute tile, and
1673
+ // a misdetected "face" zoomed to card size ruins the event picture
1552
1674
  const target = {
1553
1675
  subject: best.box,
1676
+ base: best.parentBox ?? best.box,
1554
1677
  hint: best.parentBox ? directionBetween(best.box, best.parentBox) : undefined,
1555
1678
  };
1556
- // the attribute tile was cut from this frame with this very target, so the
1557
- // card is already encoded; only the 16:9 companion is still missing
1558
- const rendered = await this.renderMoment(target, analysis, bestCard);
1679
+ const rendered = await this.renderMoment(target, analysis);
1559
1680
  if (!rendered)
1560
1681
  return;
1561
1682
  const stream = analysis.isMainStream ? 'main' : 'low';
@@ -1619,10 +1740,42 @@ let DetectionCoordinator = class DetectionCoordinator {
1619
1740
  const motionPlugin = this.plugins.get(SensorType.Motion);
1620
1741
  if (!motionPlugin?.requiresFrames)
1621
1742
  return undefined;
1743
+ return this.scaleForMotionInput(rawFrame);
1744
+ }
1745
+ async scaleForMotionInput(rawFrame) {
1622
1746
  const maxWidth = MOTION_WIDTH_MAP[this.motionResolution];
1623
1747
  const scaled = await this.frameScaler.scaleProportional(rawFrame, maxWidth, 'gray');
1624
1748
  return scaled ? this.frameScaler.toVideoFrameData(scaled) : undefined;
1625
1749
  }
1750
+ feedLocalizer(gray, t0, ptzSuppressed) {
1751
+ this.lastLocalizeAt = t0;
1752
+ const data = Buffer.isBuffer(gray.data) ? gray.data : Buffer.from(gray.data);
1753
+ const boxes = this.localizer.localize({ data, width: gray.width, height: gray.height });
1754
+ // during ego-motion the diff is garbage, feeding keeps the reference alive
1755
+ this.anchorBoxes = ptzSuppressed ? [] : boxes;
1756
+ }
1757
+ async seedLocalizerReference() {
1758
+ if (!this.localizerWanted || !this.frameSource.decodeOldestKeyframe)
1759
+ return;
1760
+ try {
1761
+ const frame = await this.frameSource.decodeOldestKeyframe();
1762
+ if (!frame)
1763
+ return;
1764
+ try {
1765
+ const gray = await this.scaleForMotionInput(frame);
1766
+ if (gray) {
1767
+ const data = Buffer.isBuffer(gray.data) ? gray.data : Buffer.from(gray.data);
1768
+ this.localizer.seedReference({ data, width: gray.width, height: gray.height });
1769
+ }
1770
+ }
1771
+ finally {
1772
+ frame[Symbol.dispose]?.();
1773
+ }
1774
+ }
1775
+ catch (error) {
1776
+ this.logger.debug('Localizer reference seed failed:', error);
1777
+ }
1778
+ }
1626
1779
  trackDetectionCadence() {
1627
1780
  const now = Date.now();
1628
1781
  const previous = this.lastObjectCallAt;
@@ -1656,6 +1809,43 @@ let DetectionCoordinator = class DetectionCoordinator {
1656
1809
  return undefined;
1657
1810
  return this.scaleFrameForPlugin(rawFrame, objectPlugin, scaler);
1658
1811
  }
1812
+ async runObjectDetection(objectPlugin, analysis, pluginMotionBoxes) {
1813
+ const inputSpec = objectPlugin.modelSpec?.input;
1814
+ const motionAnchors = [...this.anchorBoxes, ...pluginMotionBoxes.map((d) => d.box).filter((box) => box !== undefined), ...this.farewellBoxes];
1815
+ this.farewellBoxes = [];
1816
+ const windows = isVideoInputSpec(inputSpec)
1817
+ ? this.detectionWindow.plan(motionAnchors, this.trackAnchorBoxes, analysis.frame.width, analysis.frame.height, inputSpec.width)
1818
+ : null;
1819
+ if (windows && windows.length > 0) {
1820
+ this.perf.zoomTicks++;
1821
+ this.perf.zoomWindows += windows.length;
1822
+ detectionRecord.zoom({ kind: 'object', anchors: motionAnchors, tracks: this.trackAnchorBoxes, windows });
1823
+ const inferStart = Date.now();
1824
+ const perWindow = await Promise.all(windows.map((window) => this.detectInWindow(objectPlugin, analysis.frame, analysis.scaler, window, inputSpec)));
1825
+ this.perf.objectMs += Date.now() - inferStart;
1826
+ this.perf.objectCount++;
1827
+ return mergeWindowDetections(perWindow.flat());
1828
+ }
1829
+ const scaleStart = Date.now();
1830
+ const objectFrame = await this.scaleForObject(analysis.frame, analysis.scaler);
1831
+ this.perf.scaleMs += Date.now() - scaleStart;
1832
+ if (!objectFrame || !this.loopRunning)
1833
+ return undefined;
1834
+ const inferStart = Date.now();
1835
+ const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
1836
+ this.perf.objectMs += Date.now() - inferStart;
1837
+ this.perf.objectCount++;
1838
+ return FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
1839
+ }
1840
+ async detectInWindow(plugin, frame, scaler, window, spec) {
1841
+ const scaleStart = Date.now();
1842
+ const cropped = await scaler.cropToSpec(frame, window, spec);
1843
+ this.perf.scaleMs += Date.now() - scaleStart;
1844
+ if (!cropped)
1845
+ return [];
1846
+ const result = await PromiseTimeout(plugin.proxy.detectObjects(scaler.toVideoFrameData(cropped.padded, 'model')), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
1847
+ return FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), cropped.geometry);
1848
+ }
1659
1849
  async scaleFrameForPlugin(rawFrame, plugin, scaler = this.frameScaler) {
1660
1850
  const inputSpec = plugin.modelSpec?.input;
1661
1851
  if (!isVideoInputSpec(inputSpec))
@@ -1672,27 +1862,34 @@ let DetectionCoordinator = class DetectionCoordinator {
1672
1862
  ingestAssistedObjects(sensorId, objects) {
1673
1863
  if (!objects.assisted)
1674
1864
  return;
1865
+ // the assist returns raw detector output; the frame path dedupes via NMS
1866
+ // before ingest, so this path must merge too or near-identical candidate
1867
+ // boxes inflate the object count
1868
+ const merged = this.pipeline.runMergeAndZoneFilter(objects.detections);
1675
1869
  const refiltered = this.applyExternalDetectionFilters(SensorType.Object, {
1676
- detected: objects.detections.length > 0,
1677
- detections: objects.detections,
1870
+ detected: merged.length > 0,
1871
+ detections: merged,
1872
+ });
1873
+ detectionRecord.tick({
1874
+ assistIngest: {
1875
+ raw: objects.detections.map((d) => ({ label: d.label, score: d.confidence, box: d.box })),
1876
+ merged: merged.length,
1877
+ kept: (refiltered.detections ?? []).length,
1878
+ },
1678
1879
  });
1679
1880
  this.ingestDetectionResult(SensorType.Object, sensorId, refiltered);
1680
1881
  }
1681
- async runObjectAssist(frame, reported) {
1882
+ async runObjectAssist(frame, reported, scaler = this.frameScaler) {
1682
1883
  if (!this.plugins.hasEligibleObjectAssist()) {
1683
1884
  return { detections: reported, assisted: false };
1684
1885
  }
1685
1886
  const assist = this.plugins.get(SensorType.ObjectAssist);
1686
- const scaled = await this.scaleFrameForPlugin(frame, assist);
1687
- if (!scaled)
1688
- return { detections: reported, assisted: false };
1689
1887
  try {
1690
1888
  const inferStart = Date.now();
1691
- const result = await PromiseTimeout(assist.proxy.detectObjects(scaled.model), DETECT_TIMEOUT_MS, undefined, `Object assist timed out after ${DETECT_TIMEOUT_MS}ms`);
1889
+ const boxed = await this.assistDetections(assist, frame, scaler, reported);
1692
1890
  this.perf.assistMs += Date.now() - inferStart;
1693
1891
  this.perf.assistCount++;
1694
1892
  const reportedLabels = new Set(reported.map((d) => d.label.toLowerCase()));
1695
- const boxed = FrameScaler.undoLetterbox(ensureDetectionBoxes(result?.detections ?? []), scaled.geometry);
1696
1893
  const found = boxed.filter((d) => reportedLabels.size === 0 || reportedLabels.has(d.label.toLowerCase()));
1697
1894
  if (found.length === 0)
1698
1895
  return { detections: reported, assisted: false };
@@ -1706,6 +1903,31 @@ let DetectionCoordinator = class DetectionCoordinator {
1706
1903
  return { detections: reported, assisted: false };
1707
1904
  }
1708
1905
  }
1906
+ async assistDetections(assist, frame, scaler, reported) {
1907
+ const inputSpec = assist.modelSpec?.input;
1908
+ const reportedBoxes = reported.map((d) => d.box).filter((box) => box !== undefined);
1909
+ // a coordinate-less report still has the localizer's diff anchors: zoom
1910
+ // the assist into the moving regions instead of letterboxing the whole
1911
+ // frame, which shrinks the subject to nothing on a main-stream frame
1912
+ const anchors = reportedBoxes.length > 0 ? reportedBoxes : [...this.anchorBoxes, ...this.farewellBoxes];
1913
+ const pad = reportedBoxes.length > 0 ? TRACK_PAD : MOTION_PAD;
1914
+ if (isVideoInputSpec(inputSpec) && anchors.length > 0) {
1915
+ const windows = planWindowsOnce(anchors, pad, frame.width, frame.height, inputSpec.width);
1916
+ if (windows.length > 0) {
1917
+ detectionRecord.zoom({ kind: 'assist', anchors, windows });
1918
+ const perWindow = await Promise.all(windows.map((window) => this.detectInWindow(assist, frame, scaler, window, inputSpec)));
1919
+ return mergeWindowDetections(perWindow.flat());
1920
+ }
1921
+ }
1922
+ detectionRecord.tick({
1923
+ assistFullFrame: { anchors: anchors.length, videoSpec: isVideoInputSpec(inputSpec), loop: this.loopRunning, cascade: this.cascade.isActive },
1924
+ });
1925
+ const scaled = await this.scaleFrameForPlugin(frame, assist, scaler);
1926
+ if (!scaled)
1927
+ return [];
1928
+ const result = await PromiseTimeout(assist.proxy.detectObjects(scaled.model), DETECT_TIMEOUT_MS, undefined, `Object assist timed out after ${DETECT_TIMEOUT_MS}ms`);
1929
+ return FrameScaler.undoLetterbox(ensureDetectionBoxes(result?.detections ?? []), scaled.geometry);
1930
+ }
1709
1931
  };
1710
1932
  __decorate([
1711
1933
  RPCMethod,