@camera.ui/server 0.0.6 → 0.0.9

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 (238) hide show
  1. package/dist/interface/assets/{@tanstack-BqH4GyO-.js → @tanstack-B-9PO-kT.js} +1 -1
  2. package/dist/interface/assets/{@vee-validate-DCBdnGo-.js → @vee-validate-D_G0ehOl.js} +1 -1
  3. package/dist/interface/assets/{@vue-C7-KaDG4.js → @vue-C10tA4b9.js} +6 -6
  4. package/dist/interface/assets/{@vueform-arggO0eF.js → @vueform-Bj73kRPS.js} +1 -1
  5. package/dist/interface/assets/{@vueuse-C3WMTDqL.js → @vueuse-BPeqNGoM.js} +1 -1
  6. package/dist/interface/assets/Account-MEaGtu1D.js +1 -0
  7. package/dist/interface/assets/Adminpanel-D1ycltxf.js +1 -0
  8. package/dist/interface/assets/Appearance-Jlzp_P21.js +1 -0
  9. package/dist/interface/assets/Backup-1-ju9NDo.js +1 -0
  10. package/dist/interface/assets/Camera-BOu5dUel.js +1 -0
  11. package/dist/interface/assets/Cameras-C7eUXdZk.js +1 -0
  12. package/dist/interface/assets/Cameras-tmTzi1M6.css +1 -0
  13. package/dist/interface/assets/Camview-CEgwGCGR.js +1 -0
  14. package/dist/interface/assets/{Camview-vUAs5YHU.css → Camview-CWTrsoY0.css} +1 -1
  15. package/dist/interface/assets/Config-Dt-etQ3h.js +1 -0
  16. package/dist/interface/assets/{Console-Caz7y-T0.js → Console-FCve5CKy.js} +2 -2
  17. package/dist/interface/assets/FirstSteps-DLkKDSe9.js +1 -0
  18. package/dist/interface/assets/{NewCamera-B9t-0NCc.js → NewCamera-Cc1X79Ns.js} +2 -2
  19. package/dist/interface/assets/Plugin-CnQjafMC.js +1 -0
  20. package/dist/interface/assets/{Plugin-BSHKKjrl.css → Plugin-DzKKCoqQ.css} +1 -1
  21. package/dist/interface/assets/Plugins-B8gCgxOx.css +1 -0
  22. package/dist/interface/assets/Plugins-CwyUdMlv.js +1 -0
  23. package/dist/interface/assets/Recordings-Boeev1lV.js +1 -0
  24. package/dist/interface/assets/Recordings-D5ta7AYS.js +1 -0
  25. package/dist/interface/assets/Settings-Cvm2jnBr.js +1 -0
  26. package/dist/interface/assets/System-fHQ7XKri.js +1 -0
  27. package/dist/interface/assets/User-5o1VKizc.js +1 -0
  28. package/dist/interface/assets/VConfigEditor.vue_vue_type_script_setup_true_lang-C_95ELTZ.js +2 -0
  29. package/dist/interface/assets/VConsole-BHy92Ws0.js +1 -0
  30. package/dist/interface/assets/VConsole-MV9pQtJj.css +1 -0
  31. package/dist/interface/assets/{VPlayer-YdRn97Bs.js → VPlayer-LX5HGLrY.js} +1 -1
  32. package/dist/interface/assets/{VPluginCard--3bx6UJb.js → VPluginCard-CFpg0LM4.js} +2 -2
  33. package/dist/interface/assets/VPluginCard-Fl0AYmnF.css +1 -0
  34. package/dist/interface/assets/main-BCWSiwnj.js +9 -0
  35. package/dist/interface/assets/{main-B3IVLctZ.css → main-CQNVVncO.css} +1 -1
  36. package/dist/interface/assets/{pinia-D8MtiGcH.js → pinia-CitQ5b66.js} +2 -2
  37. package/dist/interface/assets/{plugins-Bf03y242.js → plugins-D80RNVmI.js} +1 -1
  38. package/dist/interface/assets/qrcode-SHonoxyK.js +8 -0
  39. package/dist/interface/assets/{system-BONnDA4y.js → system-CS_ZOs3_.js} +1 -1
  40. package/dist/interface/assets/{ui-CNpIJotI.js → ui-CHKoHLl-.js} +1 -1
  41. package/dist/interface/assets/{v-calendar-uh0aTGqP.js → v-calendar-C-8W4FeM.js} +1 -1
  42. package/dist/interface/assets/{vee-validate-D3FqbChK.js → vee-validate-Ck3I2guh.js} +1 -1
  43. package/dist/interface/assets/{vue-i18n-1AU8MPys.js → vue-i18n-DuCDz9TJ.js} +1 -1
  44. package/dist/interface/assets/{vue-inline-svg-2DDwYW6V.js → vue-inline-svg-C2gR6nRD.js} +1 -1
  45. package/dist/interface/assets/{vue-router-DfeQ8s4I.js → vue-router-OPGDMnws.js} +1 -1
  46. package/dist/interface/assets/{vue3-dnd-BwxcSU9Z.js → vue3-dnd-Hf295yge.js} +1 -1
  47. package/dist/interface/assets/{vue3-toastify-Ul-rf53I.js → vue3-toastify-DQr2dOCx.js} +1 -1
  48. package/dist/interface/assets/vuetify-CrcT8VHB.js +5 -0
  49. package/dist/interface/index.html +14 -15
  50. package/dist/interface/sw.js +1 -1
  51. package/dist/server/bin/cui-service.d.ts +1 -0
  52. package/dist/server/bin/cui-service.js +41 -9
  53. package/dist/server/bin/cui-service.js.map +1 -1
  54. package/dist/server/package.json +8 -7
  55. package/dist/server/requirements.txt +2 -3
  56. package/dist/server/src/api/controllers/cameras.controller.js +2 -2
  57. package/dist/server/src/api/controllers/cameras.controller.js.map +1 -1
  58. package/dist/server/src/api/controllers/plugins.controller.js +7 -6
  59. package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
  60. package/dist/server/src/api/go2rtc/api/streams.d.ts +4 -4
  61. package/dist/server/src/api/go2rtc/api/streams.js +2 -24
  62. package/dist/server/src/api/go2rtc/api/streams.js.map +1 -1
  63. package/dist/server/src/api/go2rtc/types.d.ts +6 -2
  64. package/dist/server/src/api/schemas/go2rtc.schema.d.ts +5 -5
  65. package/dist/server/src/api/services/cameras.service.d.ts +6 -5
  66. package/dist/server/src/api/services/cameras.service.js +72 -59
  67. package/dist/server/src/api/services/cameras.service.js.map +1 -1
  68. package/dist/server/src/api/services/plugins.service.js +11 -13
  69. package/dist/server/src/api/services/plugins.service.js.map +1 -1
  70. package/dist/server/src/api.d.ts +2 -2
  71. package/dist/server/src/camera/controller.d.ts +9 -4
  72. package/dist/server/src/camera/controller.js +21 -7
  73. package/dist/server/src/camera/controller.js.map +1 -1
  74. package/dist/server/src/camera/device.d.ts +15 -3
  75. package/dist/server/src/camera/device.js.map +1 -1
  76. package/dist/server/src/camera/index.d.ts +18 -39
  77. package/dist/server/src/camera/index.js +137 -165
  78. package/dist/server/src/camera/index.js.map +1 -1
  79. package/dist/server/src/camera/interfaces/camera.d.ts +0 -1
  80. package/dist/server/src/camera/interfaces/camera.js +0 -8
  81. package/dist/server/src/camera/interfaces/camera.js.map +1 -1
  82. package/dist/server/src/camera/streaming/werift-session.d.ts +10 -1
  83. package/dist/server/src/camera/streaming/werift-session.js +16 -1
  84. package/dist/server/src/camera/streaming/werift-session.js.map +1 -1
  85. package/dist/server/src/camera/types.d.ts +0 -1
  86. package/dist/server/src/camera/types.js.map +1 -1
  87. package/dist/server/src/camera/videoFrame.d.ts +3 -3
  88. package/dist/server/src/camera/videoFrame.js +6 -7
  89. package/dist/server/src/camera/videoFrame.js.map +1 -1
  90. package/dist/server/src/decoder/types.d.ts +4 -9
  91. package/dist/server/src/decoder/worker.d.ts +3 -5
  92. package/dist/server/src/decoder/worker.js +40 -64
  93. package/dist/server/src/decoder/worker.js.map +1 -1
  94. package/dist/server/src/go2rtc/index.js +1 -1
  95. package/dist/server/src/go2rtc/index.js.map +1 -1
  96. package/dist/server/src/index.d.ts +3 -3
  97. package/dist/server/src/main.js +1 -2
  98. package/dist/server/src/main.js.map +1 -1
  99. package/dist/server/src/nats/connection.d.ts +17 -0
  100. package/dist/server/src/nats/connection.js +66 -0
  101. package/dist/server/src/nats/connection.js.map +1 -0
  102. package/dist/server/src/nats/error.d.ts +9 -0
  103. package/dist/server/src/nats/error.js +11 -0
  104. package/dist/server/src/nats/error.js.map +1 -0
  105. package/dist/server/src/nats/index.d.ts +6 -4
  106. package/dist/server/src/nats/index.js +22 -17
  107. package/dist/server/src/nats/index.js.map +1 -1
  108. package/dist/server/src/nats/messageQueue.d.ts +7 -4
  109. package/dist/server/src/nats/messageQueue.js +43 -59
  110. package/dist/server/src/nats/messageQueue.js.map +1 -1
  111. package/dist/server/src/nats/proxy/cameraDevice.js +19 -24
  112. package/dist/server/src/nats/proxy/cameraDevice.js.map +1 -1
  113. package/dist/server/src/nats/proxy/deviceManager.d.ts +2 -2
  114. package/dist/server/src/nats/proxy/deviceManager.js +4 -14
  115. package/dist/server/src/nats/proxy/deviceManager.js.map +1 -1
  116. package/dist/server/src/nats/server.js +4 -10
  117. package/dist/server/src/nats/server.js.map +1 -1
  118. package/dist/server/src/nats/subscription.d.ts +12 -0
  119. package/dist/server/src/nats/subscription.js +21 -0
  120. package/dist/server/src/nats/subscription.js.map +1 -0
  121. package/dist/server/src/nats/types.d.ts +25 -40
  122. package/dist/server/src/nats/types.js +1 -10
  123. package/dist/server/src/nats/types.js.map +1 -1
  124. package/dist/server/src/nats/utils.d.ts +2 -4
  125. package/dist/server/src/nats/utils.js +3 -3
  126. package/dist/server/src/nats/utils.js.map +1 -1
  127. package/dist/server/src/plugins/index.d.ts +3 -5
  128. package/dist/server/src/plugins/index.js +54 -141
  129. package/dist/server/src/plugins/index.js.map +1 -1
  130. package/dist/server/src/plugins/plugin.d.ts +4 -13
  131. package/dist/server/src/plugins/plugin.js +40 -90
  132. package/dist/server/src/plugins/plugin.js.map +1 -1
  133. package/dist/server/src/plugins/types.d.ts +4 -0
  134. package/dist/server/src/plugins/types.js.map +1 -1
  135. package/dist/server/src/plugins/worker.d.ts +1 -1
  136. package/dist/server/src/plugins/worker.js +52 -66
  137. package/dist/server/src/plugins/worker.js.map +1 -1
  138. package/dist/server/src/polyglot/node/decoder/child.js +87 -159
  139. package/dist/server/src/polyglot/node/decoder/child.js.map +1 -1
  140. package/dist/server/src/polyglot/node/decoder/imageUtils.d.ts +1 -1
  141. package/dist/server/src/polyglot/node/plugins/cameraStorage.d.ts +2 -2
  142. package/dist/server/src/polyglot/node/plugins/cameraStorage.js.map +1 -1
  143. package/dist/server/src/polyglot/node/plugins/child.d.ts +1 -1
  144. package/dist/server/src/polyglot/node/plugins/child.js +34 -60
  145. package/dist/server/src/polyglot/node/plugins/child.js.map +1 -1
  146. package/dist/server/src/polyglot/node/plugins/{config.js → configService.js} +1 -1
  147. package/dist/server/src/polyglot/node/plugins/configService.js.map +1 -0
  148. package/dist/server/src/polyglot/node/plugins/{api.d.ts → pluginApi.d.ts} +9 -12
  149. package/dist/server/src/polyglot/node/plugins/pluginApi.js +34 -0
  150. package/dist/server/src/polyglot/node/plugins/pluginApi.js.map +1 -0
  151. package/dist/server/src/polyglot/node/plugins/{logger.d.ts → pluginLogger.d.ts} +2 -0
  152. package/dist/server/src/polyglot/node/plugins/pluginLogger.js +89 -0
  153. package/dist/server/src/polyglot/node/plugins/pluginLogger.js.map +1 -0
  154. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.d.ts +8 -11
  155. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js +67 -129
  156. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js.map +1 -1
  157. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.d.ts +6 -8
  158. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.js +15 -43
  159. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.js.map +1 -1
  160. package/dist/server/src/polyglot/node/plugins/storageController.d.ts +2 -2
  161. package/dist/server/src/polyglot/node/plugins/storageController.js.map +1 -1
  162. package/dist/server/src/polyglot/python/camera/interfaces/camera.py +0 -6
  163. package/dist/server/src/polyglot/python/camera/video_frame.py +10 -18
  164. package/dist/server/src/polyglot/python/camera_ui_types/__init__.py +1 -5
  165. package/dist/server/src/polyglot/python/decoder/child.py +102 -161
  166. package/dist/server/src/polyglot/python/plugins/camera_storage.py +10 -23
  167. package/dist/server/src/polyglot/python/plugins/child.py +35 -60
  168. package/dist/server/src/polyglot/python/plugins/{proxy/message_queue.py → message_queue.py} +50 -81
  169. package/dist/server/src/polyglot/python/plugins/plugin_api.py +13 -21
  170. package/dist/server/src/polyglot/python/plugins/proxy/camera_device.py +90 -173
  171. package/dist/server/src/polyglot/python/plugins/proxy/device_manager.py +25 -64
  172. package/dist/server/src/polyglot/python/plugins/storage_controller.py +3 -4
  173. package/dist/server/src/polyglot/python/utilities/connection.py +86 -0
  174. package/dist/server/src/polyglot/python/utilities/object_path.py +12 -4
  175. package/dist/server/src/polyglot/python/utilities/subscriptions.py +20 -0
  176. package/dist/server/src/polyglot/python/utilities/task.py +11 -1
  177. package/dist/server/src/services/config/index.js +4 -2
  178. package/dist/server/src/services/config/index.js.map +1 -1
  179. package/dist/server/src/services/logger/index.js +11 -8
  180. package/dist/server/src/services/logger/index.js.map +1 -1
  181. package/dist/server/src/utils/ffmpeg.d.ts +2 -0
  182. package/dist/server/src/{decoder → utils}/ffmpeg.js +2 -2
  183. package/dist/server/src/utils/ffmpeg.js.map +1 -0
  184. package/dist/server/src/utils/pythonInstaller.d.ts +1 -1
  185. package/dist/server/src/utils/pythonInstaller.js +10 -4
  186. package/dist/server/src/utils/pythonInstaller.js.map +1 -1
  187. package/dist/server/src/utils/reader.js.map +1 -0
  188. package/dist/server/src/utils/subscribed.d.ts +4 -4
  189. package/dist/server/src/utils/subscribed.js.map +1 -1
  190. package/package.json +8 -7
  191. package/dist/interface/assets/@camera.ui-2NVnv26v.js +0 -8
  192. package/dist/interface/assets/Account-gJpkf0nE.js +0 -1
  193. package/dist/interface/assets/Adminpanel-Bge9nu5N.js +0 -1
  194. package/dist/interface/assets/Appearance-D22q-X_I.js +0 -1
  195. package/dist/interface/assets/Backup-pJm6s10G.js +0 -1
  196. package/dist/interface/assets/Camera-BgO2yRjb.js +0 -1
  197. package/dist/interface/assets/Cameras-BM3vSM_R.css +0 -1
  198. package/dist/interface/assets/Cameras-DjeKruR_.js +0 -1
  199. package/dist/interface/assets/Camview-DfzlprzN.js +0 -1
  200. package/dist/interface/assets/Config-BKIOIkjC.js +0 -1
  201. package/dist/interface/assets/FirstSteps-mQK4XFTs.js +0 -1
  202. package/dist/interface/assets/Plugin-Di_bBsg-.js +0 -1
  203. package/dist/interface/assets/Plugins-BsMITags.css +0 -1
  204. package/dist/interface/assets/Plugins-DhmzMNuO.js +0 -1
  205. package/dist/interface/assets/Recordings-Bu53bSBp.js +0 -1
  206. package/dist/interface/assets/Recordings-XY20w8yp.js +0 -1
  207. package/dist/interface/assets/Settings-gGRBRVST.js +0 -1
  208. package/dist/interface/assets/System-ZDrcLNk0.js +0 -1
  209. package/dist/interface/assets/User-D4vEhhzi.js +0 -1
  210. package/dist/interface/assets/VConfigEditor.vue_vue_type_script_setup_true_lang-CXFI-IeE.js +0 -2
  211. package/dist/interface/assets/VConsole-BHo3xprq.js +0 -1
  212. package/dist/interface/assets/VConsole-f-nUkOuD.css +0 -1
  213. package/dist/interface/assets/VPluginCard-BJYMuCLk.css +0 -1
  214. package/dist/interface/assets/encode-utf8-Cn5aGd7v.js +0 -1
  215. package/dist/interface/assets/main-C4rYiRNz.js +0 -2
  216. package/dist/interface/assets/qrcode-CGiDONbL.js +0 -8
  217. package/dist/interface/assets/vuetify-_-Oqac3B.js +0 -5
  218. package/dist/server/src/decoder/ffmpeg.d.ts +0 -2
  219. package/dist/server/src/decoder/ffmpeg.js.map +0 -1
  220. package/dist/server/src/nats/proxy/index.d.ts +0 -2
  221. package/dist/server/src/nats/proxy/index.js +0 -3
  222. package/dist/server/src/nats/proxy/index.js.map +0 -1
  223. package/dist/server/src/polyglot/node/decoder/reader.js.map +0 -1
  224. package/dist/server/src/polyglot/node/plugins/api.js +0 -34
  225. package/dist/server/src/polyglot/node/plugins/api.js.map +0 -1
  226. package/dist/server/src/polyglot/node/plugins/config.js.map +0 -1
  227. package/dist/server/src/polyglot/node/plugins/logger.js +0 -128
  228. package/dist/server/src/polyglot/node/plugins/logger.js.map +0 -1
  229. package/dist/server/src/polyglot/node/plugins/proxy/index.d.ts +0 -2
  230. package/dist/server/src/polyglot/node/plugins/proxy/index.js +0 -3
  231. package/dist/server/src/polyglot/node/plugins/proxy/index.js.map +0 -1
  232. package/dist/server/src/polyglot/node/plugins/proxy/messageQueue.d.ts +0 -20
  233. package/dist/server/src/polyglot/node/plugins/proxy/messageQueue.js +0 -152
  234. package/dist/server/src/polyglot/node/plugins/proxy/messageQueue.js.map +0 -1
  235. /package/dist/server/src/polyglot/node/plugins/{config.d.ts → configService.d.ts} +0 -0
  236. /package/dist/server/src/polyglot/python/plugins/{logger.py → plugin_logger.py} +0 -0
  237. /package/dist/server/src/{polyglot/node/decoder → utils}/reader.d.ts +0 -0
  238. /package/dist/server/src/{polyglot/node/decoder → utils}/reader.js +0 -0
@@ -10,18 +10,16 @@ from camera_ui_types import (
10
10
  ImageOptions,
11
11
  MotionState,
12
12
  )
13
- from nats.aio.client import Client as NatsConnection
14
- from nats.aio.msg import Msg
15
13
  from PIL import Image
16
14
  from plugins.typings import ProcessPrebufferedImageRequest
17
- from utilities.packer import pack
15
+ from utilities.connection import ProxyConnection
18
16
 
19
17
 
20
18
  class VideoFrame:
21
19
  def __init__(
22
20
  self,
23
21
  camera_id: str,
24
- publisher: NatsConnection,
22
+ publisher: ProxyConnection,
25
23
  frame_data: FrameData,
26
24
  metadata: FrameMetadata,
27
25
  prebuffer_duration: int,
@@ -57,10 +55,10 @@ class VideoFrame:
57
55
  options = self.__validate_options("buffer", options)
58
56
  image_information = self.__get_image_information(options)
59
57
 
60
- response = await self.__send(options)
58
+ data = await self.__send(options)
61
59
 
62
60
  return {
63
- "image": response.data,
61
+ "image": data,
64
62
  "info": image_information,
65
63
  }
66
64
 
@@ -76,12 +74,10 @@ class VideoFrame:
76
74
  else "RGB"
77
75
  )
78
76
 
79
- response = await self.__send(options)
77
+ data = await self.__send(options)
80
78
 
81
79
  return {
82
- "image": Image.frombytes(
83
- mode, (image_information["width"], image_information["height"]), response.data
84
- ),
80
+ "image": Image.frombytes(mode, (image_information["width"], image_information["height"]), data),
85
81
  "info": image_information,
86
82
  }
87
83
 
@@ -90,8 +86,8 @@ class VideoFrame:
90
86
  image["image"].save(path)
91
87
  image["image"].close()
92
88
 
93
- async def __send(self, options: ImageOptions) -> Msg:
94
- if not self.__publisher or not self.__publisher.is_connected:
89
+ async def __send(self, options: ImageOptions) -> bytes:
90
+ if not self.__publisher:
95
91
  raise Exception("Frame generator closed?")
96
92
 
97
93
  request: ProcessPrebufferedImageRequest = {
@@ -101,11 +97,7 @@ class VideoFrame:
101
97
  "prebufferDuration": self.__prebuffer_duration,
102
98
  }
103
99
 
104
- return await self.__publisher.request(
105
- self.__frame_generator_subject,
106
- pack(request),
107
- 10000,
108
- )
100
+ return await self.__publisher.request(self.__frame_generator_subject, request, 10000)
109
101
 
110
102
  def __validate_options(
111
103
  self,
@@ -183,7 +175,7 @@ class MotionFrame(VideoFrame):
183
175
  def __init__(
184
176
  self,
185
177
  camera_id: str,
186
- publisher: NatsConnection,
178
+ publisher: ProxyConnection,
187
179
  frame_data: FrameData,
188
180
  motion_state: MotionState,
189
181
  metadata: FrameMetadata,
@@ -646,9 +646,6 @@ class CameraDelegate(metaclass=ABCMeta):
646
646
  @abstractmethod
647
647
  async def snapshot(self) -> bytes: ...
648
648
 
649
- @abstractmethod
650
- async def reboot(self) -> None: ...
651
-
652
649
 
653
650
  CameraDelegateMethodNames = Literal["reboot", "snapshot"]
654
651
 
@@ -836,8 +833,7 @@ class CameraDevice(Protocol):
836
833
  mid_resolution_source: Optional[CameraSource]
837
834
  low_resolution_source: Optional[CameraSource]
838
835
 
839
- cameraDelegate: CameraDelegate
840
- ptzDelegate: CameraPTZDelegate
836
+ ptz: CameraPTZDelegate
841
837
 
842
838
  @overload
843
839
  def set_delegate(self, name: Literal["cameraDelegate"], delegate: CameraDelegate) -> None: ...
@@ -9,14 +9,11 @@ from contextlib import suppress
9
9
  from datetime import datetime
10
10
  from typing import Any, Literal, Optional, Union, cast
11
11
 
12
- import nats
13
12
  from camera_ui_types import DecoderFormat, FrameData, FrameMetadata, ImageOptions
14
13
  from image_utils import process_image_threaded
15
- from nats.aio.client import Client as NatsConnection
16
14
  from nats.aio.msg import Msg
17
- from nats.aio.subscription import Subscription
18
15
  from nats.errors import NoRespondersError, TimeoutError
19
- from plugins.logger import PluginLogger
16
+ from plugins.plugin_logger import PluginLogger
20
17
  from plugins.typings import ProcessPrebufferedImageRequest
21
18
  from typings import (
22
19
  BaseFrameWorkerConfig,
@@ -35,7 +32,9 @@ from typings import (
35
32
  WorkerToMainMessage,
36
33
  WorkerToMainResponse,
37
34
  )
35
+ from utilities.connection import ProxyConnection
38
36
  from utilities.packer import pack, unpack
37
+ from utilities.subscriptions import ProxySubscription
39
38
  from utilities.task import TaskSet
40
39
 
41
40
  config: BaseFrameWorkerConfig = {
@@ -73,14 +72,14 @@ class FrameWorkerChild:
73
72
  self.signaling_subject = f"{config['cameraId']}.frameGenerator.signaling"
74
73
  self.frame_subject = f"{config['cameraId']}.frameReceiver"
75
74
 
76
- self.publisher: Optional[NatsConnection] = None
77
- self.signaling_publisher: Optional[NatsConnection] = None
78
- self.image_publisher: Optional[NatsConnection] = None
79
- self.frame_publisher: Optional[NatsConnection] = None
75
+ self.publisher: Optional[ProxyConnection] = None
76
+ self.signaling_publisher: Optional[ProxyConnection] = None
77
+ self.image_publisher: Optional[ProxyConnection] = None
78
+ self.frame_publisher: Optional[ProxyConnection] = None
80
79
 
81
- self.subscriber: Optional[Subscription] = None
82
- self.image_subscriber: Optional[Subscription] = None
83
- self.signal_subscriber: Optional[Subscription] = None
80
+ self.subscriber: Optional[ProxySubscription] = None
81
+ self.image_subscriber: Optional[ProxySubscription] = None
82
+ self.signal_subscriber: Optional[ProxySubscription] = None
84
83
 
85
84
  self.max_diff_frame = 3
86
85
  self.restart_delay = 5
@@ -105,6 +104,12 @@ class FrameWorkerChild:
105
104
  self.stdout_task: Optional[asyncio.Task[None]] = None
106
105
  self.close_task: Optional[asyncio.Task[None]] = None
107
106
 
107
+ self.restart_timeout: Optional[asyncio.TimerHandle] = None
108
+ self.restart_delay = 10
109
+
110
+ self.close_timeout: Optional[asyncio.TimerHandle] = None
111
+ self.close_delay = 5
112
+
108
113
  @property
109
114
  def log_prefix(self) -> str:
110
115
  return f"{self.camera_name}:"
@@ -119,72 +124,30 @@ class FrameWorkerChild:
119
124
  async def listen(self) -> None:
120
125
  self.setup_signal_handlers()
121
126
 
122
- async def ignore_cb(e: Optional[Any] = None):
123
- pass
127
+ servers = [endpoint for endpoint in config["proxyEndpoints"] if endpoint.startswith("nats://")]
124
128
 
125
- self.publisher = await nats.connect(
126
- servers=[endpoint for endpoint in config["proxyEndpoints"] if endpoint.startswith("nats://")],
127
- name=self.subject,
128
- user=config["auth"]["user"],
129
- password=config["auth"]["password"],
130
- max_reconnect_attempts=-1,
131
- error_cb=ignore_cb,
132
- disconnected_cb=ignore_cb,
133
- closed_cb=ignore_cb,
134
- reconnected_cb=ignore_cb,
135
- discovered_server_cb=ignore_cb,
136
- )
129
+ self.publisher = ProxyConnection(self.subject, servers, config["auth"])
130
+ await self.publisher.connect()
137
131
 
138
- self.image_publisher = await nats.connect(
139
- servers=[endpoint for endpoint in config["proxyEndpoints"] if endpoint.startswith("nats://")],
140
- name=self.image_subject,
141
- user=config["auth"]["user"],
142
- password=config["auth"]["password"],
143
- max_reconnect_attempts=-1,
144
- error_cb=ignore_cb,
145
- disconnected_cb=ignore_cb,
146
- closed_cb=ignore_cb,
147
- reconnected_cb=ignore_cb,
148
- discovered_server_cb=ignore_cb,
149
- )
132
+ self.image_publisher = ProxyConnection(self.image_subject, servers, config["auth"])
133
+ await self.image_publisher.connect()
150
134
 
151
- self.frame_publisher = await nats.connect(
152
- servers=[endpoint for endpoint in config["proxyEndpoints"] if endpoint.startswith("nats://")],
153
- name=self.frame_subject,
154
- user=config["auth"]["user"],
155
- password=config["auth"]["password"],
156
- max_reconnect_attempts=-1,
157
- error_cb=ignore_cb,
158
- disconnected_cb=ignore_cb,
159
- closed_cb=ignore_cb,
160
- reconnected_cb=ignore_cb,
161
- discovered_server_cb=ignore_cb,
162
- )
135
+ self.frame_publisher = ProxyConnection(self.frame_subject, servers, config["auth"])
136
+ await self.frame_publisher.connect()
163
137
 
164
- self.signaling_publisher = await nats.connect(
165
- servers=[endpoint for endpoint in config["proxyEndpoints"] if endpoint.startswith("nats://")],
166
- name=self.signaling_subject,
167
- user=config["auth"]["user"],
168
- password=config["auth"]["password"],
169
- max_reconnect_attempts=-1,
170
- error_cb=ignore_cb,
171
- disconnected_cb=ignore_cb,
172
- closed_cb=ignore_cb,
173
- reconnected_cb=ignore_cb,
174
- discovered_server_cb=ignore_cb,
175
- )
138
+ self.signaling_publisher = ProxyConnection(self.signaling_subject, servers, config["auth"])
139
+ await self.signaling_publisher.connect()
176
140
 
177
141
  self.subscriber = await self.publisher.subscribe(subject=self.subject)
142
+ self.subscriber.on("message", self.on_worker_message)
143
+
178
144
  self.image_subscriber = await self.image_publisher.subscribe(subject=self.image_subject)
179
- self.signal_subscriber = await self.signaling_publisher.subscribe(subject=self.signaling_subject)
145
+ self.image_subscriber.on("message", self.on_image_request)
180
146
 
181
- await asyncio.gather(
182
- self.send("started"),
183
- self.listen_to_ipc_messages(),
184
- self.listen_to_signaling_messages(),
185
- self.listen_to_prebuffered_image_messages(),
186
- )
147
+ self.signal_subscriber = await self.signaling_publisher.subscribe(subject=self.signaling_subject)
148
+ self.signal_subscriber.on("message", self.on_signaling_message)
187
149
 
150
+ await self.send("started")
188
151
  await self.shutdown_event.wait()
189
152
 
190
153
  async def close(self) -> None:
@@ -209,13 +172,14 @@ class FrameWorkerChild:
209
172
  async def start(self) -> None:
210
173
  try:
211
174
  self.closed = False
175
+
176
+ if self.close_timeout:
177
+ self.close_timeout.cancel()
178
+ self.close_timeout = None
179
+
212
180
  await self.start_video_session()
213
181
  except Exception as e:
214
- self.logger.error(
215
- f"{self.log_prefix} An error occurred while starting frame session:",
216
- e,
217
- )
218
-
182
+ self.logger.error(f"{self.log_prefix} An error occurred while starting frame session:", e)
219
183
  await self.restart_ffmpeg()
220
184
 
221
185
  async def stop(self) -> None:
@@ -237,6 +201,14 @@ class FrameWorkerChild:
237
201
  self._metadata = None
238
202
 
239
203
  async def reset_video_session(self) -> None:
204
+ if self.close_timeout:
205
+ self.close_timeout.cancel()
206
+ self.close_timeout = None
207
+
208
+ if self.restart_timeout:
209
+ self.restart_timeout.cancel()
210
+ self.restart_timeout = None
211
+
240
212
  if self.stderr_task:
241
213
  self.stderr_task.cancel()
242
214
  self.stderr_task = None
@@ -256,8 +228,7 @@ class FrameWorkerChild:
256
228
  self.ffmpeg_process = None
257
229
 
258
230
  async def start_video_session(self) -> None:
259
- if self.ffmpeg_process:
260
- return
231
+ await self.reset_video_session()
261
232
 
262
233
  # await self.reset()
263
234
  self.stopped = False
@@ -267,9 +238,6 @@ class FrameWorkerChild:
267
238
  await asyncio.wait_for(self.metadata_event.wait(), timeout=15)
268
239
  self.logger.debug(f"{self.log_prefix} Frame session started")
269
240
  except asyncio.TimeoutError:
270
- ffmpeg_process = cast(Optional[asyncio.subprocess.Process], self.ffmpeg_process)
271
- if ffmpeg_process and ffmpeg_process.returncode is None:
272
- ffmpeg_process.kill()
273
241
  raise Exception("FFmpeg process timed out") from None
274
242
 
275
243
  def build_ffmpeg_args(self, ffmpeg_path: str, camera_source: str, ffmpeg_args: FfmpegArgs) -> list[str]:
@@ -335,7 +303,7 @@ class FrameWorkerChild:
335
303
  self.close_task = self.tasks.add(self.handle_ffmpeg_close())
336
304
 
337
305
  async def restart_ffmpeg(self) -> None:
338
- if not self.stopped and not self.closed and not self.restarting:
306
+ if not self.stopped and not self.closed and not self.restarting and len(self.clients) > 0:
339
307
  self.restarting = True
340
308
 
341
309
  self.logger.log(f"{self.log_prefix} Restarting FFmpeg process in {self.restart_delay} seconds...")
@@ -343,7 +311,7 @@ class FrameWorkerChild:
343
311
  with suppress(asyncio.TimeoutError):
344
312
  await asyncio.wait_for(asyncio.sleep(self.restart_delay), timeout=self.restart_delay)
345
313
 
346
- if not self.stopped and not self.closed:
314
+ if not self.stopped and not self.closed and len(self.clients) > 0:
347
315
  await self.start()
348
316
 
349
317
  self.restarting = False
@@ -392,32 +360,40 @@ class FrameWorkerChild:
392
360
  async def handle_ffmpeg_stdout(self) -> None:
393
361
  try:
394
362
  metadata = (await self.metadata).copy()
395
- restart_timeout = None
363
+ warned = False
396
364
 
397
365
  def is_closed() -> bool:
398
366
  return (
399
367
  self.stopped
400
368
  or self.closed
401
369
  or not self.frame_publisher
402
- or self.frame_publisher.is_closed
403
370
  or not self.ffmpeg_process
404
371
  or not self.ffmpeg_process.stdout
405
372
  or self.ffmpeg_process.returncode is not None
406
373
  )
407
374
 
375
+ def reset_restart_watchdog():
376
+ if self.restart_timeout:
377
+ self.restart_timeout.cancel()
378
+ self.restart_timeout = None
379
+
408
380
  def restart_watchdog():
409
- nonlocal restart_timeout
410
- if restart_timeout:
411
- restart_timeout.cancel()
412
- restart_timeout = asyncio.get_event_loop().call_later(
413
- 3, lambda: asyncio.create_task(restart_ffmpeg())
381
+ reset_restart_watchdog()
382
+ self.restart_timeout = asyncio.get_event_loop().call_later(
383
+ self.restart_delay, lambda: asyncio.create_task(restart_ffmpeg())
414
384
  )
415
385
 
416
386
  async def restart_ffmpeg():
417
387
  if not self.stopped and not self.closed and not self.restarting:
418
- self.logger.error(
419
- f"{self.log_prefix} FFmpeg process is not producing frames, restarting..."
420
- )
388
+ message = "FFmpeg process is not producing frames, terminating..."
389
+
390
+ nonlocal warned
391
+ if warned is False:
392
+ self.logger.debug(f"{self.log_prefix} {message}")
393
+ warned = True
394
+ else:
395
+ self.logger.error(f"{self.log_prefix} {message}")
396
+
421
397
  await self.reset()
422
398
  await self.restart_ffmpeg()
423
399
 
@@ -429,7 +405,10 @@ class FrameWorkerChild:
429
405
  int(metadata["frameSize"]),
430
406
  )
431
407
 
408
+ warned = False
409
+
432
410
  if is_closed():
411
+ reset_restart_watchdog()
433
412
  break
434
413
 
435
414
  frame_id = self.generate_id()
@@ -459,7 +438,7 @@ class FrameWorkerChild:
459
438
  if self.prebuffer_full:
460
439
  frame_data: FrameData = {"frameId": frame_id, "timestamp": now}
461
440
 
462
- await self.frame_publisher.publish(self.frame_subject, pack(frame_data)) # type: ignore
441
+ await self.frame_publisher.publish(self.frame_subject, frame_data) # type: ignore
463
442
 
464
443
  except asyncio.IncompleteReadError as e:
465
444
  if not is_closed() and not self.restarting:
@@ -473,6 +452,8 @@ class FrameWorkerChild:
473
452
  e,
474
453
  )
475
454
  break
455
+ finally:
456
+ reset_restart_watchdog()
476
457
  except asyncio.CancelledError:
477
458
  pass
478
459
 
@@ -480,9 +461,6 @@ class FrameWorkerChild:
480
461
  try:
481
462
  if self.ffmpeg_process:
482
463
  code = await self.ffmpeg_process.wait()
483
- self.ffmpeg_process = None
484
-
485
- await self.reset()
486
464
 
487
465
  if code != 0 and code != -9 or not self.stopped:
488
466
  self.logger.error(
@@ -494,6 +472,9 @@ class FrameWorkerChild:
494
472
  f"{self.log_prefix} FFmpeg process has exited with code {code} (expected)"
495
473
  )
496
474
 
475
+ self.ffmpeg_process = None
476
+
477
+ await self.reset()
497
478
  await self.restart_ffmpeg()
498
479
  except asyncio.CancelledError:
499
480
  pass
@@ -547,14 +528,10 @@ class FrameWorkerChild:
547
528
  "data": data,
548
529
  }
549
530
 
550
- msg = await self.publisher.request(
551
- self.ipc_subject,
552
- pack(worker_message),
553
- timeout=30,
531
+ response: MainToWorkerResponse = await self.publisher.request(
532
+ self.ipc_subject, worker_message, timeout=30
554
533
  )
555
534
 
556
- response: MainToWorkerResponse = unpack(msg.data)
557
-
558
535
  return response.get("response")
559
536
 
560
537
  async def add_client(self, plugin_id: str, request_id: str) -> None:
@@ -593,7 +570,12 @@ class FrameWorkerChild:
593
570
  await self.send("updateClient", {"pluginId": client.plugin_id, "requestIds": client.request_ids})
594
571
 
595
572
  if len(self.clients) == 0:
596
- await self.stop()
573
+ if self.close_timeout:
574
+ self.close_timeout.cancel()
575
+
576
+ self.close_timeout = asyncio.get_event_loop().call_later(
577
+ self.close_delay, lambda: asyncio.create_task(self.stop())
578
+ )
597
579
 
598
580
  async def recover_clients(self, clients: list[RecoveredClient]) -> None:
599
581
  for client in clients:
@@ -617,7 +599,7 @@ class FrameWorkerChild:
617
599
 
618
600
  response: SignalingResponse = {"type": "disconnect", "requestId": request_id}
619
601
 
620
- await self.signaling_publisher.publish(ping_target_subject, pack(response))
602
+ await self.signaling_publisher.publish(ping_target_subject, response)
621
603
  await self.remove_client(plugin_id, request_id)
622
604
 
623
605
  async def disconnect_all_clients(self) -> None:
@@ -653,22 +635,20 @@ class FrameWorkerChild:
653
635
 
654
636
  while self.signaling_publisher and request_id_exists():
655
637
  try:
656
- ping_message: PingMessage = {"type": "ping", "requestId": request_id}
638
+ message: PingMessage = {"type": "ping", "requestId": request_id}
657
639
 
658
640
  # self.logger.trace(f"{self.log_prefix} Sending ping to {camera_id}:{plugin_id}")
659
641
 
660
- msg = await self.signaling_publisher.request(
642
+ response: PongMessage = await self.signaling_publisher.request(
661
643
  ping_target_subject,
662
- pack(ping_message),
644
+ message,
663
645
  timeout=ping_timeout,
664
646
  )
665
647
 
666
- ping_response: PongMessage = unpack(msg.data)
667
-
668
648
  if not request_id_exists():
669
649
  break
670
650
 
671
- if ping_response["type"] != "pong":
651
+ if response["type"] != "pong":
672
652
  raise Exception("Invalid response to ping")
673
653
 
674
654
  # self.logger.trace(f"{self.log_prefix} Received pong from {camera_id}:{plugin_id}")
@@ -684,62 +664,23 @@ class FrameWorkerChild:
684
664
 
685
665
  break
686
666
 
687
- async def listen_to_ipc_messages(self) -> None:
688
- if not self.subscriber:
689
- raise Exception("Subscriber not initialized")
690
-
691
- async for msg in self.subscriber.messages:
692
- try:
693
- if self.closed:
694
- break
695
-
696
- if len(msg.data) == 0:
697
- continue
698
-
699
- self.tasks.add(self.respond_on_ipc_message(msg))
700
- except Exception as e:
701
- if not self.closed:
702
- self.logger.error(f"{self.log_prefix} Error while processing IPC message:", e)
703
- else:
704
- break
705
-
706
- async def listen_to_signaling_messages(self) -> None:
707
- if not self.signal_subscriber:
708
- raise Exception("Subscriber not initialized")
709
-
710
- async for msg in self.signal_subscriber.messages:
711
- try:
712
- if self.closed:
713
- break
714
-
715
- if len(msg.data) == 0:
716
- continue
667
+ async def on_worker_message(self, msg: Msg) -> None:
668
+ if self.closed:
669
+ return
717
670
 
718
- self.tasks.add(self.respond_on_signaling_message(msg))
719
- except Exception as e:
720
- if not self.closed:
721
- self.logger.error(f"{self.log_prefix} Error while processing signaling message:", e)
722
- else:
723
- break
671
+ self.tasks.add(self.respond_on_ipc_message(msg))
724
672
 
725
- async def listen_to_prebuffered_image_messages(self) -> None:
726
- if not self.image_subscriber:
727
- raise Exception("Subscriber not initialized")
673
+ async def on_signaling_message(self, msg: Msg) -> None:
674
+ if self.closed:
675
+ return
728
676
 
729
- async for msg in self.image_subscriber.messages:
730
- try:
731
- if self.closed:
732
- break
677
+ self.tasks.add(self.respond_on_signaling_message(msg))
733
678
 
734
- if len(msg.data) == 0:
735
- continue
679
+ async def on_image_request(self, msg: Msg) -> None:
680
+ if self.closed:
681
+ return
736
682
 
737
- self.tasks.add(self.respond_on_prebuffered_image_message(msg))
738
- except Exception as e:
739
- if not self.closed:
740
- self.logger.error(f"{self.log_prefix} Error while processing image message:", e)
741
- else:
742
- break
683
+ self.tasks.add(self.respond_on_prebuffered_image_message(msg))
743
684
 
744
685
  async def respond_on_ipc_message(self, msg: Msg) -> None:
745
686
  try:
@@ -788,10 +729,10 @@ class FrameWorkerChild:
788
729
  processed_image = await self.process_frame(
789
730
  target_frame["frame"], width, height, format, options
790
731
  )
791
- await msg.respond(processed_image)
732
+ await msg.respond(pack(processed_image))
792
733
  return
793
734
 
794
- await msg.respond(b"")
735
+ await msg.respond(pack(b""))
795
736
  except Exception as e:
796
737
  self.logger.error(f"{self.log_prefix} Error while processing image respond:", e)
797
738
 
@@ -14,7 +14,6 @@ from camera_ui_types import (
14
14
  )
15
15
  from deepdiff import DeepDiff
16
16
  from lmdbm import Lmdb
17
- from logger import PluginLogger
18
17
  from plugin_api import PluginAPI
19
18
  from schema import (
20
19
  config_path_to_schema_path,
@@ -61,7 +60,6 @@ class CameraStorage:
61
60
  def __init__(
62
61
  self,
63
62
  api: PluginAPI,
64
- logger: PluginLogger,
65
63
  instance: Any,
66
64
  camera_id: str,
67
65
  schema: Optional[PluginJsonSchemaForm] = None,
@@ -69,7 +67,6 @@ class CameraStorage:
69
67
  if schema is None:
70
68
  schema = {}
71
69
  self.__api = api
72
- self.__logger = logger
73
70
  self.__instance = instance
74
71
  self.__camera_id = camera_id
75
72
  self.__schema: PluginJsonSchemaForm = schema or {}
@@ -96,8 +93,8 @@ class CameraStorage:
96
93
  try:
97
94
  config: Any = self.__db.get("config") or {}
98
95
  camera_config = config.get(self.__camera_id, {})
99
- except Exception as e:
100
- self.__logger.error("Error loading config from database:", e)
96
+ except: # noqa: E722
97
+ pass
101
98
 
102
99
  schema_config = generate_json_from_root_schema({"schema": self.__schema})
103
100
 
@@ -426,29 +423,19 @@ class CameraStorage:
426
423
  )
427
424
 
428
425
  def __save_db(self) -> None:
429
- try:
430
- config: Any = self.__db.get("config") or {}
431
- storable_config = self.__filter_storable_values(self.__schema)
426
+ config: Any = self.__db.get("config") or {}
427
+ storable_config = self.__filter_storable_values(self.__schema)
432
428
 
433
- config[self.__camera_id] = storable_config
429
+ config[self.__camera_id] = storable_config
434
430
 
435
- self.__db.update(
436
- {
437
- "config": config,
438
- }
439
- )
440
- except Exception as e:
441
- self.__logger.error("Error saving to database:", e)
431
+ self.__db.update({"config": config})
442
432
 
443
433
  def __remove_db(self) -> None:
444
- try:
445
- config: Any = self.__db.get("config") or {}
434
+ config: Any = self.__db.get("config") or {}
446
435
 
447
- if self.__camera_id in config:
448
- del config[self.__camera_id]
449
- self.__db.update(config)
450
- except Exception as e:
451
- self.__logger.error("Error removing from database:", e)
436
+ if self.__camera_id in config:
437
+ del config[self.__camera_id]
438
+ self.__db.update(config)
452
439
 
453
440
  def __deselected(self, id: str) -> None:
454
441
  if id == self.__camera_id: