@camera.ui/server 0.0.16 → 0.0.17

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 (235) hide show
  1. package/dist/interface/{@tanstack.BjdVg-TE.0.0.16.js → @tanstack.Y8LHKTFF.0.0.17.js} +1 -1
  2. package/dist/interface/{@vee-validate.BY4_2lEs.0.0.16.js → @vee-validate.BcG8e5-Z.0.0.17.js} +1 -1
  3. package/dist/interface/{@vueform.DFvJBWIi.0.0.16.js → @vueform.Cnqje5-g.0.0.17.js} +1 -1
  4. package/dist/interface/{@vueuse.CGN12qog.0.0.16.js → @vueuse.CxdA24o0.0.0.17.js} +1 -1
  5. package/dist/interface/Account.K0D29-KE.0.0.17.js +1 -0
  6. package/dist/interface/{Adminpanel.FTycvGGP.0.0.16.js → Adminpanel.CaLBYPHc.0.0.17.js} +2 -2
  7. package/dist/interface/{Appearance.DZdFEn29.0.0.16.js → Appearance.-L_mn_-o.0.0.17.js} +1 -1
  8. package/dist/interface/{Backup.C63DnD6l.0.0.16.js → Backup.DjU1SUNp.0.0.17.js} +1 -1
  9. package/dist/interface/{Camera.ClZr_XH5.0.0.16.js → Camera.BgoyeEmQ.0.0.17.js} +1 -1
  10. package/dist/interface/Cameras.CTzvFhZB.0.0.17.js +1 -0
  11. package/dist/interface/{Camview.Bo4mLtoT.0.0.16.js → Camview.AIHZOcpe.0.0.17.js} +1 -1
  12. package/dist/interface/Config.Dwreik6Q.0.0.17.js +1 -0
  13. package/dist/interface/{Console.8oN5H7vV.0.0.16.js → Console.65ILbQYd.0.0.17.js} +1 -1
  14. package/dist/interface/FirstSteps.CLRJ9qAX.0.0.17.js +1 -0
  15. package/dist/interface/{NewCamera.0K90OojR.0.0.16.js → NewCamera.C3vbU4C7.0.0.17.js} +1 -1
  16. package/dist/interface/Plugin.B5hYYBYQ.0.0.17.js +1 -0
  17. package/dist/interface/{Plugins.49uEjmzr.0.0.16.js → Plugins.CfRQtEq_.0.0.17.js} +1 -1
  18. package/dist/interface/Recordings.BbFFnSvi.0.0.17.js +1 -0
  19. package/dist/interface/Recordings.BfuhtdIj.0.0.17.js +1 -0
  20. package/dist/interface/{Settings.LCEZwyhh.0.0.16.js → Settings.BdOXqaEX.0.0.17.js} +1 -1
  21. package/dist/interface/System.ysu799BZ.0.0.17.js +4 -0
  22. package/dist/interface/User.D7asFmwE.0.0.17.js +1 -0
  23. package/dist/interface/{VConfigEditor.vue_vue_type_script_setup_true_lang.J5gKGR9C.0.0.16.js → VConfigEditor.vue_vue_type_script_setup_true_lang.BAxsYS8n.0.0.17.js} +2 -2
  24. package/dist/interface/{VConsole.BStq4qh-.0.0.16.js → VConsole.C7xGb1ES.0.0.17.js} +1 -1
  25. package/dist/interface/VPlayer.Dhm7j4pg.0.0.17.js +1 -0
  26. package/dist/interface/{VPluginCard.DR63SC8V.0.0.16.js → VPluginCard.Drmi3fPn.0.0.17.js} +1 -1
  27. package/dist/interface/{ace-builds.BhnhUUTY.0.0.16.js → ace-builds.BwzeyovQ.0.0.17.js} +1 -1
  28. package/dist/interface/assets/{Account.0.0.16.2KfYXKTQ.css → Account.0.0.17.BCEftDNE.css} +1 -1
  29. package/dist/interface/assets/{Cameras.0.0.16.D7bnvH42.css → Cameras.0.0.17.Djx0F2MM.css} +1 -1
  30. package/dist/interface/assets/{Camview.0.0.16.cgjDTjXy.css → Camview.0.0.17.Ce_V-Nxj.css} +1 -1
  31. package/dist/interface/assets/{Config.0.0.16.BQJF6s_V.css → Config.0.0.17.k4QpWumg.css} +1 -1
  32. package/dist/interface/assets/FirstSteps.0.0.17.27JF_jaE.css +1 -0
  33. package/dist/interface/assets/{Plugin.0.0.16.BGZE7hpr.css → Plugin.0.0.17.CY2rlLw0.css} +1 -1
  34. package/dist/interface/assets/User.0.0.17.DAaNwzw2.css +1 -0
  35. package/dist/interface/assets/{VPlayer.0.0.16.CzZme_fu.css → VPlayer.0.0.17.CY8lPcC-.css} +1 -1
  36. package/dist/interface/assets/{main.0.0.16.DYbE3rQA.css → main.0.0.17.CkZIZCwa.css} +1 -1
  37. package/dist/interface/axios.CpM4fnec.0.0.17.js +6 -0
  38. package/dist/interface/{dnd-core.B69q2Tfk.0.0.16.js → dnd-core.BE3BG1Ef.0.0.17.js} +1 -1
  39. package/dist/interface/{draggabilly.DKBJ_DRn.0.0.16.js → draggabilly.lOopoogm.0.0.17.js} +1 -1
  40. package/dist/interface/{engine.io-client.D47Vn3PY.0.0.16.js → engine.io-client.D9WQML-S.0.0.17.js} +1 -1
  41. package/dist/interface/{ev-emitter.CO554XzW.0.0.16.js → ev-emitter.DS-zFO5E.0.0.17.js} +1 -1
  42. package/dist/interface/{fast-deep-equal.CjfEj15C.0.0.16.js → fast-deep-equal.BEv36PJS.0.0.17.js} +1 -1
  43. package/dist/interface/{highlight.js.CsG_3GKA.0.0.16.js → highlight.js.7wLgKNe8.0.0.17.js} +1 -1
  44. package/dist/interface/index.html +33 -33
  45. package/dist/interface/{linkify-it.D-Re04U7.0.0.16.js → linkify-it.D8o-3IGu.0.0.17.js} +1 -1
  46. package/dist/interface/main.tEGILaMo.0.0.17.js +9 -0
  47. package/dist/interface/manifest.webmanifest +1 -1
  48. package/dist/interface/{markdown-it.BSj0kuDJ.0.0.16.js → markdown-it.B4HcXc4c.0.0.17.js} +1 -1
  49. package/dist/interface/{nprogress.CN7SpneM.0.0.16.js → nprogress.YYjE43ko.0.0.17.js} +1 -1
  50. package/dist/interface/{pinia.DHXyXuKc.0.0.16.js → pinia.BN10dGtz.0.0.17.js} +1 -1
  51. package/dist/interface/{plugins.FSy44vXn.0.0.16.js → plugins.DGem6WnK.0.0.17.js} +1 -1
  52. package/dist/interface/{qrcode.DjqZuH8F.0.0.16.js → qrcode.D-tn4nkj.0.0.17.js} +1 -1
  53. package/dist/interface/{react-dnd-touch-backend.Cecw_0Kl.0.0.16.js → react-dnd-touch-backend.BYa4rNzf.0.0.17.js} +1 -1
  54. package/dist/interface/{rxjs.tqVqva9T.0.0.16.js → rxjs.DfibtzOq.0.0.17.js} +1 -1
  55. package/dist/interface/{socket.io-client.BFAMFJUC.0.0.16.js → socket.io-client.C_uPgwFs.0.0.17.js} +1 -1
  56. package/dist/interface/{socket.io-parser.B5BeMqb6.0.0.16.js → socket.io-parser.BCv62i4g.0.0.17.js} +1 -1
  57. package/dist/interface/sw.js +1 -1
  58. package/dist/interface/system.QcA_yDW7.0.0.17.js +1 -0
  59. package/dist/interface/{ui.CcXHo2vy.0.0.16.js → ui.CsoNsn0g.0.0.17.js} +1 -1
  60. package/dist/interface/{unidragger.B3Aj7lJB.0.0.16.js → unidragger.CE1ihPjj.0.0.17.js} +1 -1
  61. package/dist/interface/{v-calendar.y5PNpUm6.0.0.16.js → v-calendar.uhLzWnBK.0.0.17.js} +1 -1
  62. package/dist/interface/{vee-validate.VFk2ifr-.0.0.16.js → vee-validate.Dks-nw4k.0.0.17.js} +1 -1
  63. package/dist/interface/{vue-i18n.BRzZ63Xi.0.0.16.js → vue-i18n.C41_xexi.0.0.17.js} +1 -1
  64. package/dist/interface/{vue-inline-svg.De0HvU9m.0.0.16.js → vue-inline-svg.BKM1fqUr.0.0.17.js} +1 -1
  65. package/dist/interface/{vue-router.Cr6qPC8Y.0.0.16.js → vue-router.BPduPoYE.0.0.17.js} +1 -1
  66. package/dist/interface/{vue3-apexcharts.BMb8nnby.0.0.16.js → vue3-apexcharts.CUMFs3zS.0.0.17.js} +1 -1
  67. package/dist/interface/{vue3-dnd.SEoyozNj.0.0.16.js → vue3-dnd.BNLgSXFo.0.0.17.js} +1 -1
  68. package/dist/interface/{vue3-toastify.DUisHb3e.0.0.16.js → vue3-toastify.D4rGDrYT.0.0.17.js} +1 -1
  69. package/dist/interface/{vuetify.DByERR9S.0.0.16.js → vuetify.C_gaTn8T.0.0.17.js} +1 -1
  70. package/dist/server/bin/cameraui.d.ts +4 -0
  71. package/dist/server/bin/cameraui.js +47 -33
  72. package/dist/server/bin/cameraui.js.map +1 -1
  73. package/dist/server/bin/installer/darwin.d.ts +4 -0
  74. package/dist/server/bin/installer/darwin.js +37 -29
  75. package/dist/server/bin/installer/darwin.js.map +1 -1
  76. package/dist/server/bin/installer/freebsd.d.ts +4 -5
  77. package/dist/server/bin/installer/freebsd.js +23 -33
  78. package/dist/server/bin/installer/freebsd.js.map +1 -1
  79. package/dist/server/bin/installer/linux.d.ts +7 -0
  80. package/dist/server/bin/installer/linux.js +53 -27
  81. package/dist/server/bin/installer/linux.js.map +1 -1
  82. package/dist/server/bin/installer/win32.d.ts +4 -0
  83. package/dist/server/bin/installer/win32.js +21 -14
  84. package/dist/server/bin/installer/win32.js.map +1 -1
  85. package/dist/server/package.json +4 -4
  86. package/dist/server/src/api/controllers/backup.controller.js +2 -2
  87. package/dist/server/src/api/controllers/backup.controller.js.map +1 -1
  88. package/dist/server/src/api/controllers/plugins.controller.js +42 -40
  89. package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
  90. package/dist/server/src/api/controllers/system.controller.d.ts +3 -2
  91. package/dist/server/src/api/controllers/system.controller.js +19 -21
  92. package/dist/server/src/api/controllers/system.controller.js.map +1 -1
  93. package/dist/server/src/api/controllers/users.controller.js +12 -1
  94. package/dist/server/src/api/controllers/users.controller.js.map +1 -1
  95. package/dist/server/src/api/database/index.js +5 -4
  96. package/dist/server/src/api/database/index.js.map +1 -1
  97. package/dist/server/src/api/database/types.d.ts +7 -2
  98. package/dist/server/src/api/plugins/system.plugin.js +1 -4
  99. package/dist/server/src/api/plugins/system.plugin.js.map +1 -1
  100. package/dist/server/src/api/routes/system.routes.js +2 -2
  101. package/dist/server/src/api/routes/system.routes.js.map +1 -1
  102. package/dist/server/src/api/schemas/cameras.schema.d.ts +15 -15
  103. package/dist/server/src/api/schemas/cameras.schema.js +2 -2
  104. package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
  105. package/dist/server/src/api/schemas/go2rtc.schema.d.ts +14 -14
  106. package/dist/server/src/api/schemas/users.schema.d.ts +2 -2
  107. package/dist/server/src/api/schemas/users.schema.js +4 -15
  108. package/dist/server/src/api/schemas/users.schema.js.map +1 -1
  109. package/dist/server/src/api/services/backup.service.js +16 -15
  110. package/dist/server/src/api/services/backup.service.js.map +1 -1
  111. package/dist/server/src/api/services/cameras.service.js +10 -2
  112. package/dist/server/src/api/services/cameras.service.js.map +1 -1
  113. package/dist/server/src/api/utils/cert.d.ts +1 -1
  114. package/dist/server/src/api/utils/cert.js +14 -14
  115. package/dist/server/src/api/utils/cert.js.map +1 -1
  116. package/dist/server/src/api/utils/moveFiles.js +6 -5
  117. package/dist/server/src/api/utils/moveFiles.js.map +1 -1
  118. package/dist/server/src/api/websocket/nsp/logs.js +5 -4
  119. package/dist/server/src/api/websocket/nsp/logs.js.map +1 -1
  120. package/dist/server/src/api/websocket/nsp/server.js +3 -2
  121. package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
  122. package/dist/server/src/camera/index.js +2 -0
  123. package/dist/server/src/camera/index.js.map +1 -1
  124. package/dist/server/src/camera/iou.d.ts +2 -0
  125. package/dist/server/src/camera/iou.js +53 -0
  126. package/dist/server/src/camera/iou.js.map +1 -0
  127. package/dist/server/src/camera/streaming/peer-connection.d.ts +3 -1
  128. package/dist/server/src/camera/streaming/peer-connection.js +12 -7
  129. package/dist/server/src/camera/streaming/peer-connection.js.map +1 -1
  130. package/dist/server/src/camera/streaming/webrtc-connection.d.ts +2 -0
  131. package/dist/server/src/camera/streaming/webrtc-connection.js +28 -10
  132. package/dist/server/src/camera/streaming/webrtc-connection.js.map +1 -1
  133. package/dist/server/src/camera/types.d.ts +2 -1
  134. package/dist/server/src/camera/types.js.map +1 -1
  135. package/dist/server/src/main.js +8 -4
  136. package/dist/server/src/main.js.map +1 -1
  137. package/dist/server/src/nats/server.js +4 -3
  138. package/dist/server/src/nats/server.js.map +1 -1
  139. package/dist/server/src/plugins/index.js +12 -9
  140. package/dist/server/src/plugins/index.js.map +1 -1
  141. package/dist/server/src/plugins/plugin.js +6 -4
  142. package/dist/server/src/plugins/plugin.js.map +1 -1
  143. package/dist/server/src/plugins/worker.d.ts +1 -0
  144. package/dist/server/src/plugins/worker.js +24 -16
  145. package/dist/server/src/plugins/worker.js.map +1 -1
  146. package/dist/server/src/polyglot/node/decoder/child.js +16 -10
  147. package/dist/server/src/polyglot/node/decoder/child.js.map +1 -1
  148. package/dist/server/src/polyglot/node/decoder/imageUtils.js +2 -2
  149. package/dist/server/src/polyglot/node/decoder/imageUtils.js.map +1 -1
  150. package/dist/server/src/polyglot/node/plugins/cameraStorage.d.ts +0 -1
  151. package/dist/server/src/polyglot/node/plugins/cameraStorage.js +1 -11
  152. package/dist/server/src/polyglot/node/plugins/cameraStorage.js.map +1 -1
  153. package/dist/server/src/polyglot/node/plugins/child.js +3 -3
  154. package/dist/server/src/polyglot/node/plugins/child.js.map +1 -1
  155. package/dist/server/src/polyglot/node/plugins/configService.d.ts +1 -1
  156. package/dist/server/src/polyglot/node/plugins/configService.js +5 -5
  157. package/dist/server/src/polyglot/node/plugins/configService.js.map +1 -1
  158. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.d.ts +4 -4
  159. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js +55 -46
  160. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js.map +1 -1
  161. package/dist/server/src/polyglot/python/camera/iou.py +80 -0
  162. package/dist/server/src/polyglot/python/camera_ui_types/__init__.py +15 -9
  163. package/dist/server/src/polyglot/python/decoder/child.py +20 -12
  164. package/dist/server/src/polyglot/python/plugins/camera_storage.py +7 -27
  165. package/dist/server/src/polyglot/python/plugins/child.py +32 -20
  166. package/dist/server/src/polyglot/python/plugins/config_service.py +8 -1
  167. package/dist/server/src/polyglot/python/plugins/proxy/camera_device.py +88 -53
  168. package/dist/server/src/polyglot/python/plugins/typings.py +6 -0
  169. package/dist/server/src/polyglot/python/utilities/subscriptions.py +6 -4
  170. package/dist/server/src/polyglot/python/utilities/utils.py +49 -36
  171. package/dist/server/src/services/config/index.js +32 -21
  172. package/dist/server/src/services/config/index.js.map +1 -1
  173. package/dist/server/src/utils/npm.js +3 -3
  174. package/dist/server/src/utils/npm.js.map +1 -1
  175. package/dist/server/src/utils/pythonInstaller.js +16 -14
  176. package/dist/server/src/utils/pythonInstaller.js.map +1 -1
  177. package/package.json +4 -4
  178. package/dist/interface/Account.CfJ0_AE8.0.0.16.js +0 -1
  179. package/dist/interface/Cameras.Du8aeeME.0.0.16.js +0 -1
  180. package/dist/interface/Config.XeCgFEoY.0.0.16.js +0 -1
  181. package/dist/interface/FirstSteps.mXqKiWM8.0.0.16.js +0 -1
  182. package/dist/interface/Plugin.BR9zmMja.0.0.16.js +0 -1
  183. package/dist/interface/Recordings.Bi4EWX-j.0.0.16.js +0 -1
  184. package/dist/interface/Recordings.Ce60GFcU.0.0.16.js +0 -1
  185. package/dist/interface/System.BLePSJ80.0.0.16.js +0 -4
  186. package/dist/interface/User.C2qVrMxN.0.0.16.js +0 -1
  187. package/dist/interface/VPlayer.B3YorBp_.0.0.16.js +0 -1
  188. package/dist/interface/assets/FirstSteps.0.0.16.DFSydMqk.css +0 -1
  189. package/dist/interface/assets/User.0.0.16.CXUSYt8i.css +0 -1
  190. package/dist/interface/axios.BI2i94Vb.0.0.16.js +0 -6
  191. package/dist/interface/main.CySWzYoy.0.0.16.js +0 -9
  192. package/dist/interface/system.DlYVX2qG.0.0.16.js +0 -1
  193. /package/dist/interface/{@babel.l0sNRNKZ.0.0.16.js → @babel.l0sNRNKZ.0.0.17.js} +0 -0
  194. /package/dist/interface/{@intlify.YW_pt133.0.0.16.js → @intlify.YW_pt133.0.0.17.js} +0 -0
  195. /package/dist/interface/{@mdi.jq8isxax.0.0.16.js → @mdi.jq8isxax.0.0.17.js} +0 -0
  196. /package/dist/interface/{@popperjs.BQBsAJpH.0.0.16.js → @popperjs.BQBsAJpH.0.0.17.js} +0 -0
  197. /package/dist/interface/{@react-dnd.CcwRs9xb.0.0.16.js → @react-dnd.CcwRs9xb.0.0.17.js} +0 -0
  198. /package/dist/interface/{@socket.io.Dkula2eQ.0.0.16.js → @socket.io.Dkula2eQ.0.0.17.js} +0 -0
  199. /package/dist/interface/{@vue.CIFymae3.0.0.16.js → @vue.CIFymae3.0.0.17.js} +0 -0
  200. /package/dist/interface/{@xterm.UN9r0Y94.0.0.16.js → @xterm.Cq_3w6CG.0.0.17.js} +0 -0
  201. /package/dist/interface/assets/{@vueform.0.0.16.h7tNX9p6.css → @vueform.0.0.17.h7tNX9p6.css} +0 -0
  202. /package/dist/interface/assets/{@xterm.0.0.16.CGrrDQr5.css → @xterm.0.0.17.CGrrDQr5.css} +0 -0
  203. /package/dist/interface/assets/{Adminpanel.0.0.16.A2JXV1Ib.css → Adminpanel.0.0.17.A2JXV1Ib.css} +0 -0
  204. /package/dist/interface/assets/{Backup.0.0.16.BIwaSlv3.css → Backup.0.0.17.BIwaSlv3.css} +0 -0
  205. /package/dist/interface/assets/{Camera.0.0.16.BDtUJ-lA.css → Camera.0.0.17.BDtUJ-lA.css} +0 -0
  206. /package/dist/interface/assets/{Console.0.0.16.DWrfRWs9.css → Console.0.0.17.DWrfRWs9.css} +0 -0
  207. /package/dist/interface/assets/{NewCamera.0.0.16.CMzSwwHn.css → NewCamera.0.0.17.CMzSwwHn.css} +0 -0
  208. /package/dist/interface/assets/{Plugins.0.0.16.Cwu0aGUO.css → Plugins.0.0.17.Cwu0aGUO.css} +0 -0
  209. /package/dist/interface/assets/{Settings.0.0.16.DTxcLhIY.css → Settings.0.0.17.DTxcLhIY.css} +0 -0
  210. /package/dist/interface/assets/{VConsole.0.0.16.BZT1WyS-.css → VConsole.0.0.17.BZT1WyS-.css} +0 -0
  211. /package/dist/interface/assets/{VPluginCard.0.0.16.C2OqX7DJ.css → VPluginCard.0.0.17.C2OqX7DJ.css} +0 -0
  212. /package/dist/interface/assets/{highlight.0.0.16.DgoK_Xib.css → highlight.0.0.17.DgoK_Xib.css} +0 -0
  213. /package/dist/interface/assets/{logo-256.0.0.16.BCQk9H1-.png → logo-256.0.0.17.BCQk9H1-.png} +0 -0
  214. /package/dist/interface/assets/{logo-512.0.0.16.DLKVOU2S.png → logo-512.0.0.17.DLKVOU2S.png} +0 -0
  215. /package/dist/interface/assets/{logo.0.0.16.DmEiRk0z.png → logo.0.0.17.DmEiRk0z.png} +0 -0
  216. /package/dist/interface/assets/{mode-json.0.0.16.Br7xisR1.js → mode-json.0.0.17.Br7xisR1.js} +0 -0
  217. /package/dist/interface/assets/{mode-yaml.0.0.16.CIYL8U3d.js → mode-yaml.0.0.17.CIYL8U3d.js} +0 -0
  218. /package/dist/interface/assets/{v-calendar.0.0.16.CtSuDYZU.css → v-calendar.0.0.17.CtSuDYZU.css} +0 -0
  219. /package/dist/interface/assets/{vue3-toastify.0.0.16.DTY1DMRs.css → vue3-toastify.0.0.17.DTY1DMRs.css} +0 -0
  220. /package/dist/interface/assets/{vuetify.0.0.16.DPyVfAPv.css → vuetify.0.0.17.DPyVfAPv.css} +0 -0
  221. /package/dist/interface/{compare-versions.DqEOhma6.0.0.16.js → compare-versions.DqEOhma6.0.0.17.js} +0 -0
  222. /package/dist/interface/{dijkstrajs.D_NXgYpA.0.0.16.js → dijkstrajs.D_NXgYpA.0.0.17.js} +0 -0
  223. /package/dist/interface/{engine.io-parser.BiEtp6m2.0.0.16.js → engine.io-parser.BiEtp6m2.0.0.17.js} +0 -0
  224. /package/dist/interface/{entities.C20TfXL6.0.0.16.js → entities.C20TfXL6.0.0.17.js} +0 -0
  225. /package/dist/interface/{get-size.D1dS-IaP.0.0.16.js → get-size.D1dS-IaP.0.0.17.js} +0 -0
  226. /package/dist/interface/{mdurl.k9Sl0PQj.0.0.16.js → mdurl.k9Sl0PQj.0.0.17.js} +0 -0
  227. /package/dist/interface/{punycode.js.Dj65hjkv.0.0.16.js → punycode.js.Dj65hjkv.0.0.17.js} +0 -0
  228. /package/dist/interface/{react-dnd-html5-backend.1HQjlkwI.0.0.16.js → react-dnd-html5-backend.1HQjlkwI.0.0.17.js} +0 -0
  229. /package/dist/interface/{redux.CuxFRhX_.0.0.16.js → redux.CuxFRhX_.0.0.17.js} +0 -0
  230. /package/dist/interface/{tslib.9k7AwCpt.0.0.16.js → tslib.9k7AwCpt.0.0.17.js} +0 -0
  231. /package/dist/interface/{uc.micro.kMc2yuuw.0.0.16.js → uc.micro.kMc2yuuw.0.0.17.js} +0 -0
  232. /package/dist/interface/{utils.BvINh1mj.0.0.16.js → utils.BvINh1mj.0.0.17.js} +0 -0
  233. /package/dist/interface/{uuid.DvF23Exx.0.0.16.js → uuid.DvF23Exx.0.0.17.js} +0 -0
  234. /package/dist/interface/{vue.l0sNRNKZ.0.0.16.js → vue.l0sNRNKZ.0.0.17.js} +0 -0
  235. /package/dist/interface/{zod.BaF2dDCd.0.0.16.js → zod.BaF2dDCd.0.0.17.js} +0 -0
@@ -23,6 +23,7 @@ JsonSchemaType = Literal["string", "number", "boolean", "object", "array", "butt
23
23
 
24
24
 
25
25
  class JsonBaseSchema(TypedDict):
26
+ type: JsonSchemaType
26
27
  key: NotRequired[Optional[str]]
27
28
  title: NotRequired[Optional[str]]
28
29
  description: NotRequired[Optional[str]]
@@ -42,7 +43,7 @@ class JsonBaseSchema(TypedDict):
42
43
 
43
44
 
44
45
  class JsonSchemaString(JsonBaseSchema):
45
- type: Literal["string"]
46
+ type: Literal["string"] # type: ignore
46
47
  format: NotRequired[
47
48
  Literal["date-time", "date", "time", "email", "uuid", "ipv4", "ipv6", "password", "qrCode", "image"]
48
49
  ]
@@ -51,34 +52,34 @@ class JsonSchemaString(JsonBaseSchema):
51
52
 
52
53
 
53
54
  class JsonSchemaNumber(JsonBaseSchema):
54
- type: Literal["number"]
55
+ type: Literal["number"] # type: ignore
55
56
  minimum: NotRequired[int]
56
57
  maximum: NotRequired[int]
57
58
  step: NotRequired[float]
58
59
 
59
60
 
60
61
  class JsonSchemaBoolean(JsonBaseSchema):
61
- type: Literal["boolean"]
62
+ type: Literal["boolean"] # type: ignore
62
63
 
63
64
 
64
65
  class JsonSchemaEnum(JsonBaseSchema):
65
- type: Literal["string"]
66
+ type: Literal["string"] # type: ignore
66
67
  enum: list[str]
67
68
  multiple: NotRequired[bool]
68
69
 
69
70
 
70
71
  class JsonSchemaButton(JsonBaseSchema):
71
- type: Literal["button"]
72
+ type: Literal["button"] # type: ignore
72
73
 
73
74
 
74
75
  class JsonSchemaObject(JsonBaseSchema):
75
- type: Literal["object"]
76
+ type: Literal["object"] # type: ignore
76
77
  opened: NotRequired[bool]
77
78
  properties: NotRequired["JsonSchemaForm"]
78
79
 
79
80
 
80
81
  class JsonSchemaArray(JsonBaseSchema):
81
- type: Literal["array"]
82
+ type: Literal["array"] # type: ignore
82
83
  opened: NotRequired[bool]
83
84
  items: NotRequired["JsonSchema"]
84
85
 
@@ -117,9 +118,14 @@ class SchemaConfig(TypedDict):
117
118
 
118
119
 
119
120
  # camera object
121
+ class Go2RtcSource(TypedDict):
122
+ name: str
123
+ src: str
124
+
125
+
120
126
  class Go2RtcFFMPEGSource(TypedDict):
121
- aac: str
122
- opus: str
127
+ aac: Go2RtcSource
128
+ opus: Go2RtcSource
123
129
 
124
130
 
125
131
  class Go2RtcWSSource(TypedDict):
@@ -87,11 +87,6 @@ class FrameWorkerChild:
87
87
  self.image_subscriber: Optional[ProxySubscription] = None
88
88
  self.signal_subscriber: Optional[ProxySubscription] = None
89
89
 
90
- self.max_diff_frame = 3
91
- self.restart_delay = 5
92
- self.stopped = False
93
- self.closed = False
94
- self.restarting = False
95
90
  self.clients: dict[str, FrameWorkerClient] = {}
96
91
  self.tasks: TaskSet = TaskSet(f"{self.config['cameraName']} FrameWorkerChild")
97
92
 
@@ -110,8 +105,15 @@ class FrameWorkerChild:
110
105
  self.stdout_task: Optional[asyncio.Task[None]] = None
111
106
  self.close_task: Optional[asyncio.Task[None]] = None
112
107
 
108
+ self.stopped = False
109
+ self.closed = False
110
+ self.restarting = False
111
+
112
+ self.max_diff_frame = 3
113
+ self.restart_delay = 5
114
+
113
115
  self.restart_timeout: Optional[asyncio.TimerHandle] = None
114
- self.restart_delay = 10
116
+ self.restart_watchdog_delay = 5
115
117
 
116
118
  self.close_timeout: Optional[asyncio.TimerHandle] = None
117
119
  self.close_delay = 5
@@ -236,6 +238,7 @@ class FrameWorkerChild:
236
238
  self.ffmpeg_process = None
237
239
 
238
240
  async def start_video_session(self) -> None:
241
+ self.reset_prebuffer()
239
242
  await self.reset_video_session()
240
243
 
241
244
  # await self.reset()
@@ -390,7 +393,8 @@ class FrameWorkerChild:
390
393
  def restart_watchdog():
391
394
  reset_restart_watchdog()
392
395
  self.restart_timeout = asyncio.get_event_loop().call_later(
393
- self.restart_delay, lambda: asyncio.create_task(restart_ffmpeg())
396
+ self.restart_watchdog_delay,
397
+ lambda: asyncio.create_task(restart_ffmpeg(), name="FrameWorker Restart FFmpeg"),
394
398
  )
395
399
 
396
400
  async def restart_ffmpeg():
@@ -555,9 +559,13 @@ class FrameWorkerChild:
555
559
 
556
560
  self.logger.trace(f"{self.log_prefix} New client {self.client_identifier(plugin_id, request_id)}")
557
561
 
562
+ if self.close_timeout:
563
+ self.close_timeout.cancel()
564
+ self.close_timeout = None
565
+
558
566
  await self.send("updateClient", {"pluginId": client.plugin_id, "requestIds": client.request_ids})
559
567
 
560
- if len(self.clients) == 1:
568
+ if len(self.clients) > 0 and self.ffmpeg_process is None and not self.restarting:
561
569
  await self.start()
562
570
 
563
571
  async def remove_client(self, plugin_id: str, request_id: str) -> None:
@@ -584,7 +592,7 @@ class FrameWorkerChild:
584
592
  self.close_timeout.cancel()
585
593
 
586
594
  self.close_timeout = asyncio.get_event_loop().call_later(
587
- self.close_delay, lambda: asyncio.create_task(self.stop())
595
+ self.close_delay, lambda: asyncio.create_task(self.stop(), name="FrameWorker Stop")
588
596
  )
589
597
 
590
598
  async def recover_clients(self, clients: list[RecoveredClient]) -> None:
@@ -647,7 +655,7 @@ class FrameWorkerChild:
647
655
  try:
648
656
  message: PingMessage = {"type": "ping", "requestId": request_id}
649
657
 
650
- # self.logger.trace(f"{self.log_prefix} Sending ping to {camera_id}:{plugin_id}")
658
+ # self.logger.trace(f"{self.log_prefix} Sending ping to {self.client_identifier(plugin_id, request_id)}")
651
659
 
652
660
  response: PongMessage = await self.signaling_publisher.request(
653
661
  ping_target_subject,
@@ -661,13 +669,13 @@ class FrameWorkerChild:
661
669
  if response["type"] != "pong":
662
670
  raise Exception("Invalid response to ping")
663
671
 
664
- # self.logger.trace(f"{self.log_prefix} Received pong from {camera_id}:{plugin_id}")
672
+ # self.logger.trace(f"{self.log_prefix} Received pong from {self.client_identifier(plugin_id, request_id)}")
665
673
 
666
674
  await asyncio.sleep(ping_interval)
667
675
  except (Exception, NoRespondersError, TimeoutError) as e:
668
676
  if request_id_exists():
669
677
  if isinstance(e, (NoRespondersError, TimeoutError)):
670
- self.logger.trace(f"{self.log_prefix} Ping/Pong timeout for {camera_id}:{plugin_id}")
678
+ # self.logger.trace(f"{self.log_prefix} Ping/Pong timeout for {self.client_identifier(plugin_id, request_id)}")
671
679
  await self.remove_client(plugin_id, request_id)
672
680
  else:
673
681
  self.logger.error(f"{self.log_prefix} Error during ping/pong:", e)
@@ -45,7 +45,6 @@ class CameraStorage:
45
45
  self.schema: JsonSchemaForm = schema or {}
46
46
  self.values: dict[str, Any] = {}
47
47
 
48
- self.__api.device_manager.once("cameraDeselected", self.__deselected)
49
48
  self.__api.once("shutdown", self.__close)
50
49
 
51
50
  def initializeStorage(self) -> None:
@@ -54,17 +53,10 @@ class CameraStorage:
54
53
  This will load the configuration from the database.
55
54
  """
56
55
 
57
- camera_config: dict[str, Any] = {}
58
-
59
- try:
60
- config: Any = self.__plugin_db.get("config") or {}
61
- camera_config = config.get(self.__camera_id, {})
62
- except: # noqa: E722
63
- pass
64
-
56
+ config: Any = self.__plugin_db.get("config") or {}
57
+ camera_config = config.get(self.__camera_id, {})
65
58
  schema_config = generate_json_from_root_schema({"schema": self.schema})
66
-
67
- self.values = merge_with(schema_config, camera_config, merge, [])
59
+ self.values = merge_with(schema_config, camera_config, merge)
68
60
 
69
61
  self.__save_db()
70
62
 
@@ -198,7 +190,7 @@ class CameraStorage:
198
190
  elif isinstance(schema_or_path, dict) and schema is None:
199
191
  new_schema = schema_or_path
200
192
 
201
- self.schema = merge_with(self.schema, new_schema, merge, [])
193
+ self.schema = merge_with(self.schema, new_schema, merge)
202
194
 
203
195
  await self.__resolve_on_get_functions(new_schema)
204
196
 
@@ -228,7 +220,7 @@ class CameraStorage:
228
220
  ObjectPath.delete(self.values, path)
229
221
 
230
222
  if self.__contains_storable_schema(schema):
231
- self.__remove_db()
223
+ self.__save_db()
232
224
 
233
225
  async def changeSchema(self, path: str, new_schema: dict[str, Any]) -> None:
234
226
  """
@@ -244,7 +236,7 @@ class CameraStorage:
244
236
  schema: Optional[JsonSchema] = ObjectPath.get(self.schema, schema_path) or None
245
237
 
246
238
  if schema:
247
- merge_with(schema, new_schema, merge, [])
239
+ merge_with(schema, new_schema, merge)
248
240
  ObjectPath.set(self.schema, schema_path, schema)
249
241
 
250
242
  await self.__resolve_on_get_functions(schema, schema_path)
@@ -332,7 +324,7 @@ class CameraStorage:
332
324
  schema_path = config_path_to_schema_path(new_path)
333
325
  schema_item: Optional[JsonBaseSchema] = get_value_by_path(self.schema, schema_path)
334
326
 
335
- if schema_item:
327
+ if schema_item and schema_item["type"] != "button":
336
328
  on_set_function = schema_item.get("onSet", None)
337
329
  if on_set_function:
338
330
  if inspect.iscoroutinefunction(on_set_function):
@@ -394,18 +386,6 @@ class CameraStorage:
394
386
 
395
387
  self.__plugin_db.update({"config": config})
396
388
 
397
- def __remove_db(self) -> None:
398
- config: Any = self.__plugin_db.get("config") or {}
399
-
400
- if self.__camera_id in config:
401
- del config[self.__camera_id]
402
- self.__plugin_db.update(config)
403
-
404
- def __deselected(self, id: str) -> None:
405
- if id == self.__camera_id:
406
- self.__remove_db()
407
-
408
389
  def __close(self) -> None:
409
390
  with suppress(Exception):
410
391
  self.__save_db()
411
- self.__plugin_db.close()
@@ -108,6 +108,7 @@ class PluginChild:
108
108
  self.plugin: Optional[BasePlugin] = None
109
109
 
110
110
  self.stopped = False
111
+ self.closed = False
111
112
 
112
113
  async def run(self):
113
114
  self.shutdown_event = asyncio.Event()
@@ -194,6 +195,9 @@ class PluginChild:
194
195
 
195
196
  self.stopped = True
196
197
 
198
+ if self.api:
199
+ self.api.emit("shutdown")
200
+
197
201
  if self.message_queue:
198
202
  self.message_queue.abort_queue()
199
203
 
@@ -201,12 +205,10 @@ class PluginChild:
201
205
  with suppress(Exception):
202
206
  await self.publisher.close()
203
207
 
204
- if self.api:
205
- self.api.emit("shutdown")
206
-
207
208
  if self.plugin_db:
208
- await asyncio.sleep(1)
209
- self.plugin_db.close()
209
+ await asyncio.sleep(0.5)
210
+ with suppress(Exception):
211
+ self.plugin_db.close()
210
212
 
211
213
  self.shutdown_event.set()
212
214
 
@@ -291,9 +293,27 @@ class PluginChild:
291
293
 
292
294
  if "type" in message:
293
295
  if message["type"] == PLUGIN_COMMAND.START.value and "data" in message:
294
- self.tasks.add(self.start(message["data"]))
296
+ await self.start(message["data"])
295
297
  elif message["type"] == PLUGIN_COMMAND.STOP.value:
296
- self.tasks.add(self.stop())
298
+ await self.stop()
299
+
300
+ async def gracefully_close(self, signame: str, reason: Any = "unknown"):
301
+ if self.stopped or self.closed:
302
+ return
303
+
304
+ self.closed = True
305
+
306
+ self.logger.log(
307
+ f"{self.display_name} is gracefully closing from signal='{signame}' reason='{reason}'..."
308
+ )
309
+
310
+ try:
311
+ stop_task = asyncio.create_task(self.stop(), name=f"{self.display_name} Stop")
312
+ await asyncio.wait_for(stop_task, timeout=2)
313
+ os.kill(os.getpid(), signal.SIGKILL)
314
+ except asyncio.TimeoutError:
315
+ self.logger.warn("Failed to gracefully close before timeout, force quit!")
316
+ os.kill(os.getpid(), signal.SIGKILL)
297
317
 
298
318
  def setup_signal_handlers(self):
299
319
  loop = asyncio.get_running_loop()
@@ -302,20 +322,12 @@ class PluginChild:
302
322
  for signame in ("SIGINT", "SIGTERM"):
303
323
  loop.add_signal_handler(
304
324
  getattr(signal, signame),
305
- lambda signame=signame: self.gracefully_close(signame, getattr(signal, signame)),
325
+ lambda signame=signame: asyncio.create_task(
326
+ self.gracefully_close(signame, getattr(signal, signame)),
327
+ name=f"{self.display_name} Signal",
328
+ ),
306
329
  )
307
330
 
308
- def gracefully_close(self, signame: str, reason: Any = "unknown"):
309
- if self.stopped:
310
- return
311
-
312
- self.logger.log(
313
- f"{self.display_name} is gracefully closing from signal='{signame}' reason='{reason}'..."
314
- )
315
-
316
- self.tasks.add(self.stop())
317
- self.shutdown_event.set()
318
-
319
331
  def handle_exception(self, loop: AbstractEventLoop, context: Any):
320
332
  print("Exception context:", context)
321
333
  traceback.print_exc()
@@ -331,7 +343,7 @@ class PluginChild:
331
343
  self.logger.error(f"Caught exception: {msg}")
332
344
 
333
345
  if loop.is_running():
334
- self.gracefully_close("uncaughtException", msg)
346
+ asyncio.create_task(self.gracefully_close("uncaughtException", msg))
335
347
 
336
348
 
337
349
  if __name__ == "__main__":
@@ -163,7 +163,14 @@ class PluginConfigService:
163
163
  self.set(path_segments[0], self.__config[path_segments[0]], write)
164
164
 
165
165
  def __init(self) -> None:
166
- os.makedirs(os.path.dirname(self.__config_file), exist_ok=True)
166
+ directory = os.path.dirname(self.__config_file)
167
+
168
+ if not os.path.exists(directory):
169
+ os.makedirs(directory)
170
+
171
+ if not os.path.exists(self.__config_file):
172
+ open(self.__config_file, "a").close()
173
+
167
174
  self.__read()
168
175
 
169
176
  def __read(self) -> None:
@@ -10,6 +10,7 @@ from uuid import uuid4
10
10
 
11
11
  import aiohttp
12
12
  from cachetools import LRUCache, cached
13
+ from camera.iou import merge_detections
13
14
  from camera.polygon import are_motion_detections_valid, are_object_detections_valid
14
15
  from camera.video_frame import MotionFrame, VideoFrame
15
16
  from camera_ui_types import (
@@ -68,6 +69,7 @@ from typings import (
68
69
  ClientType,
69
70
  Delegates,
70
71
  DeserializedError,
72
+ FrameSubscribtion,
71
73
  PluginInfo,
72
74
  ProxyMessageStructure,
73
75
  )
@@ -190,13 +192,13 @@ class CameraDeviceProxy:
190
192
  self.camera_subscriber: Optional[ProxySubscription] = None
191
193
  self.camera_plugin_subscriber: Optional[ProxySubscription] = None
192
194
  self.signaling_subscriber: Optional[ProxySubscription] = None
193
- self.frame_subscriber: Optional[ProxySubscription] = None
194
- self.motion_frame_subscriber: Optional[ProxySubscription] = None
195
+ self.frame_subscribers: list[FrameSubscribtion] = []
196
+ self.motion_frame_subscribers: list[FrameSubscribtion] = []
195
197
 
196
198
  self.tasks: TaskSet = TaskSet(f"{self.plugin['name']} - {self.name} - CameraDeviceProxy")
197
199
  self.message_queue: Optional[MessageQueue] = None
198
200
  self.active_requests: set[str] = set()
199
- self.active_motion_requests = 0
201
+ self.active_motion_requests: set[str] = set()
200
202
 
201
203
  # print(("INIT camera device with clientSubject", self.subject))
202
204
 
@@ -425,24 +427,24 @@ class CameraDeviceProxy:
425
427
 
426
428
  if prebuffer_duration > 0:
427
429
  request_type = "prebuffer"
428
- prebuffer_duration = 5 if prebuffer_duration > 5 else prebuffer_duration
430
+ prebuffer_duration = min(prebuffer_duration, 5)
429
431
 
430
432
  request_id = f"{str(uuid4())}-{request_type}"
431
433
 
432
434
  try:
433
435
  metadata = await self.__connect_to_decoder(request_id)
436
+ frame_subscriber = await self.frame_publisher.subscribe(self.frame_subject)
434
437
 
435
- if not self.frame_subscriber:
436
- self.frame_subscriber = await self.frame_publisher.subscribe(self.frame_subject)
438
+ self.frame_subscribers.append({"request_id": request_id, "subscriber": frame_subscriber})
437
439
 
438
- async for msg in self.frame_subscriber.subscriber.messages:
439
- try:
440
+ try:
441
+ async for msg in frame_subscriber.subscriber.messages:
440
442
  if (
441
443
  not self.initialized
442
444
  or len(self.active_requests) == 0
443
445
  or request_id not in self.active_requests
444
446
  ):
445
- break
447
+ return
446
448
 
447
449
  if len(msg.data) == 0:
448
450
  continue
@@ -450,35 +452,39 @@ class CameraDeviceProxy:
450
452
  frame_data: FrameData = unpack(msg.data)
451
453
 
452
454
  yield VideoFrame(self.id, self.frame_publisher, frame_data, metadata, prebuffer_duration)
453
- except Exception as e:
454
- if self.initialized:
455
- self.logger.error("Error while processing frame:", e)
456
- else:
457
- break
455
+
456
+ except GeneratorExit:
457
+ return
458
458
 
459
459
  except asyncio.CancelledError:
460
- pass
460
+ return
461
461
  except Exception as e:
462
462
  if self.initialized:
463
463
  self.logger.error("Error while generating frames:", e)
464
+ raise
464
465
  finally:
465
- self.tasks.add(self.__disconnect_from_decoder(request_id))
466
+ await self.__disconnect_from_decoder(request_id)
466
467
 
467
468
  async def get_motion_frames(self) -> AsyncGenerator[MotionFrame, None]:
468
469
  if not self.motion_frame_publisher:
469
470
  raise Exception("Motion Frame Publisher is not initialized")
470
471
 
471
- self.active_motion_requests += 1
472
+ request_id = f"{str(uuid4())}-motion"
472
473
 
473
474
  try:
474
- if not self.motion_frame_subscriber:
475
- self.motion_frame_subscriber = await self.motion_frame_publisher.subscribe(
476
- self.motion_frame_subject
477
- )
475
+ motion_frame_subscriber = await self.motion_frame_publisher.subscribe(self.motion_frame_subject)
478
476
 
479
- async for msg in self.motion_frame_subscriber.subscriber.messages:
480
- try:
481
- if not self.initialized or self.active_motion_requests <= 0:
477
+ self.motion_frame_subscribers.append(
478
+ {"request_id": request_id, "subscriber": motion_frame_subscriber}
479
+ )
480
+
481
+ try:
482
+ async for msg in motion_frame_subscriber.subscriber.messages:
483
+ if (
484
+ not self.initialized
485
+ or len(self.active_motion_requests) == 0
486
+ or request_id not in self.active_motion_requests
487
+ ):
482
488
  break
483
489
 
484
490
  if not msg or not msg.data or len(msg.data) == 0:
@@ -499,22 +505,30 @@ class CameraDeviceProxy:
499
505
  motion_state,
500
506
  motion_frame["metadata"],
501
507
  )
502
- except Exception as e:
503
- if self.initialized:
504
- self.logger.error("Error while processing motion frame:", e)
505
- else:
506
- break
508
+
509
+ except GeneratorExit:
510
+ return
507
511
 
508
512
  except asyncio.CancelledError:
509
513
  pass
510
514
  except Exception as e:
511
515
  if self.initialized:
512
516
  self.logger.error("Error while generating motion frames:", e)
517
+ raise
513
518
  finally:
514
- self.active_motion_requests -= 1
519
+ if request_id in self.active_motion_requests:
520
+ self.active_motion_requests.remove(request_id)
521
+
522
+ motion_frame_subscribtion = next(
523
+ (sub for sub in self.motion_frame_subscribers if sub["request_id"] == request_id), None
524
+ )
525
+
526
+ if motion_frame_subscribtion:
527
+ self.motion_frame_subscribers.remove(motion_frame_subscribtion)
528
+ await motion_frame_subscribtion["subscriber"].close()
515
529
 
516
- if self.active_motion_requests <= 0:
517
- self.tasks.add(self.__close_motion_frame_subscriber())
530
+ if len(self.motion_frame_subscribers) == 0:
531
+ await self.__close_motion_frame_subscribers()
518
532
 
519
533
  @overload
520
534
  def get_value(self, state_name: Literal["light"]) -> LightState: ...
@@ -913,6 +927,8 @@ class CameraDeviceProxy:
913
927
  det["boundingBox"] = cast(tuple[int, int, int, int], tuple(det["boundingBox"]))
914
928
  det["confidence"] = float(det["confidence"])
915
929
 
930
+ motion_event["detections"] = merge_detections(motion_event["detections"])
931
+
916
932
  motion_event["detections"] = are_motion_detections_valid(
917
933
  motion_event["detections"], self.activity_zones, self.activity_settings
918
934
  )
@@ -1433,37 +1449,48 @@ class CameraDeviceProxy:
1433
1449
  metadata = metadata_response.get("metadata")
1434
1450
 
1435
1451
  return metadata
1436
- except: # noqa: E722
1437
- self.logger.error("Failed to get metadata! Frame Worker FFmpeg process might not be running")
1452
+ except Exception as e:
1453
+ self.logger.error("Failed to get metadata! Frame Worker FFmpeg process might not be running", e)
1438
1454
 
1439
1455
  async def __disconnect_from_decoder(self, request_id: str) -> None:
1440
1456
  if request_id in self.active_requests:
1441
1457
  self.active_requests.remove(request_id)
1442
1458
 
1443
- if not self.signaling_publisher:
1444
- raise Exception("Signaling Publisher not initialized")
1459
+ frame_subscription = next(
1460
+ (sub for sub in self.frame_subscribers if sub["request_id"] == request_id), None
1461
+ )
1445
1462
 
1446
- message: SignalingRequest = {
1447
- "type": "disconnect",
1448
- "requestId": request_id,
1449
- "cameraId": self.id,
1450
- "pluginId": self.plugin["id"],
1451
- }
1463
+ if frame_subscription:
1464
+ self.frame_subscribers.remove(frame_subscription)
1465
+ await frame_subscription["subscriber"].close()
1452
1466
 
1453
- await self.signaling_publisher.publish(self.signaling_target_subject, message)
1467
+ if self.signaling_publisher:
1468
+ message: SignalingRequest = {
1469
+ "type": "disconnect",
1470
+ "requestId": request_id,
1471
+ "cameraId": self.id,
1472
+ "pluginId": self.plugin["id"],
1473
+ }
1454
1474
 
1455
- async def __close_frame_subscriber(self) -> None:
1456
- with suppress(Exception):
1457
- if self.frame_subscriber:
1458
- await self.frame_subscriber.close()
1459
- self.frame_subscriber = None
1475
+ await self.signaling_publisher.publish(self.signaling_target_subject, message)
1476
+
1477
+ async def __close_frame_subscribers(self) -> None:
1478
+ self.active_requests.clear()
1479
+
1480
+ for sub in self.frame_subscribers:
1481
+ with suppress(Exception):
1482
+ await sub["subscriber"].close()
1460
1483
 
1461
- async def __close_motion_frame_subscriber(self) -> None:
1462
- if self.motion_frame_subscriber:
1484
+ self.frame_subscribers.clear()
1485
+
1486
+ async def __close_motion_frame_subscribers(self) -> None:
1487
+ self.active_motion_requests.clear()
1488
+
1489
+ for sub in self.motion_frame_subscribers:
1463
1490
  with suppress(Exception):
1464
- await self.motion_frame_subscriber.close()
1491
+ await sub["subscriber"].close()
1465
1492
 
1466
- self.motion_frame_subscriber = None
1493
+ self.motion_frame_subscribers.clear()
1467
1494
 
1468
1495
  async def __request_handler(self, message: ProxyMessageStructure) -> None:
1469
1496
  if not self.message_queue:
@@ -1565,8 +1592,16 @@ class CameraDeviceProxy:
1565
1592
  if message["requestId"] in self.active_requests:
1566
1593
  self.active_requests.remove(message["requestId"])
1567
1594
 
1595
+ frame_subscribtion = next(
1596
+ (sub for sub in self.frame_subscribers if sub["request_id"] == message["requestId"]), None
1597
+ )
1598
+
1599
+ if frame_subscribtion:
1600
+ self.frame_subscribers.remove(frame_subscribtion)
1601
+ await frame_subscribtion["subscriber"].close()
1602
+
1568
1603
  if len(self.active_requests) == 0:
1569
- self.tasks.add(self.__close_frame_subscriber())
1604
+ self.tasks.add(self.__close_frame_subscribers())
1570
1605
 
1571
1606
  async def __respond_to_signaling_message(self, msg: Msg, message: PingMessage) -> None:
1572
1607
  try:
@@ -25,6 +25,7 @@ from camera_ui_types import (
25
25
  StateValues,
26
26
  )
27
27
  from typing_extensions import Literal, NotRequired, TypedDict
28
+ from utilities.subscriptions import ProxySubscription
28
29
 
29
30
 
30
31
  class PLUGIN_STATUS(Enum):
@@ -245,3 +246,8 @@ Delegates = Literal[
245
246
  "prebufferDelegate",
246
247
  "ptzDelegate",
247
248
  ]
249
+
250
+
251
+ class FrameSubscribtion(TypedDict):
252
+ request_id: str
253
+ subscriber: ProxySubscription
@@ -1,19 +1,21 @@
1
+ import asyncio
2
+
1
3
  from nats.aio.subscription import Subscription as NatsSubscription
2
4
  from pyeecui.asyncio import AsyncIOEventEmitter
3
5
 
4
6
 
5
7
  class ProxySubscription(AsyncIOEventEmitter):
6
- def __init__(self, subscriber: NatsSubscription) -> None:
8
+ def __init__(self, subscriber: NatsSubscription, max_concurrent: int = 25) -> None:
7
9
  super().__init__()
8
-
9
10
  self.subscriber = subscriber
11
+ self.semaphore = asyncio.Semaphore(max_concurrent)
10
12
 
11
13
  async def listen(self):
12
14
  async for msg in self.subscriber.messages:
13
15
  if len(msg.data) == 0:
14
16
  continue
15
-
16
- self.emit("message", msg)
17
+ async with self.semaphore:
18
+ self.emit("message", msg)
17
19
 
18
20
  async def close(self):
19
21
  self.remove_all_listeners()