camera.ui3 0.0.30-alpha.0 → 0.0.30-alpha.1

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 (320) hide show
  1. package/dist/interface/@camera.ui.KA9bRSoR.0.0.30-alpha.0.js +3 -0
  2. package/dist/interface/@tanstack.CTzdjGxD.0.0.30-alpha.0.js +1 -0
  3. package/dist/interface/{@vee-validate.0Cv0x9aN.0.0.30-alpha.0.js → @vee-validate.BJKTyKHK.0.0.30-alpha.0.js} +1 -1
  4. package/dist/interface/@vue.BeJw0wEm.0.0.30-alpha.0.js +17 -0
  5. package/dist/interface/{@vueform.Du_niMHk.0.0.30-alpha.0.js → @vueform.DIw88mcs.0.0.30-alpha.0.js} +1 -1
  6. package/dist/interface/{@vueuse.Dv9U068T.0.0.30-alpha.0.js → @vueuse.DK7Mz0BT.0.0.30-alpha.0.js} +1 -1
  7. package/dist/interface/Account.D23CSQgK.0.0.30-alpha.0.js +1 -0
  8. package/dist/interface/Adminpanel.CbDtQWxn.0.0.30-alpha.0.js +2 -0
  9. package/dist/interface/Appearance.az1S-8rV.0.0.30-alpha.0.js +1 -0
  10. package/dist/interface/Backup.BxjbY7JO.0.0.30-alpha.0.js +1 -0
  11. package/dist/interface/Camera.CWxxie6C.0.0.30-alpha.0.js +1 -0
  12. package/dist/interface/Cameras.Cs1dXd-W.0.0.30-alpha.0.js +1 -0
  13. package/dist/interface/Camview.uyIq-pxm.0.0.30-alpha.0.js +1 -0
  14. package/dist/interface/Config.DnuL5NIv.0.0.30-alpha.0.js +1 -0
  15. package/dist/interface/Console.C1dI5f9x.0.0.30-alpha.0.js +4 -0
  16. package/dist/interface/FirstSteps.C_TTm4lX.0.0.30-alpha.0.js +1 -0
  17. package/dist/interface/{NewCamera.Kn9biZIb.0.0.30-alpha.0.js → NewCamera.DjMFd-OB.0.0.30-alpha.0.js} +2 -2
  18. package/dist/interface/Plugin.rLO6u13g.0.0.30-alpha.0.js +1 -0
  19. package/dist/interface/Plugins.Dr-sUa9o.0.0.30-alpha.0.js +1 -0
  20. package/dist/interface/Recordings.4iG-hK8g.0.0.30-alpha.0.js +1 -0
  21. package/dist/interface/Recordings.Dxh2d-IQ.0.0.30-alpha.0.js +1 -0
  22. package/dist/interface/Settings.DAletWZO.0.0.30-alpha.0.js +1 -0
  23. package/dist/interface/System.Pv01yz8u.0.0.30-alpha.0.js +4 -0
  24. package/dist/interface/User.VLcbTFm_.0.0.30-alpha.0.js +1 -0
  25. package/dist/interface/{VConfigEditor.vue_vue_type_script_setup_true_lang.Br68bfQJ.0.0.30-alpha.0.js → VConfigEditor.vue_vue_type_script_setup_true_lang.pXHIBxVl.0.0.30-alpha.0.js} +1 -1
  26. package/dist/interface/{VConsole.BT0UxZb1.0.0.30-alpha.0.js → VConsole.BUZLhyeb.0.0.30-alpha.0.js} +1 -1
  27. package/dist/interface/VPlayer.DW5rNren.0.0.30-alpha.0.js +1 -0
  28. package/dist/interface/{VPluginCard.Bz3GmzAE.0.0.30-alpha.0.js → VPluginCard.D5f85QIk.0.0.30-alpha.0.js} +2 -2
  29. package/dist/interface/index.html +15 -14
  30. package/dist/interface/main.CNzh1Rg5.0.0.30-alpha.0.js +2 -0
  31. package/dist/interface/{pinia.DRjp4GU7.0.0.30-alpha.0.js → pinia.BPnxDxQ3.0.0.30-alpha.0.js} +2 -2
  32. package/dist/interface/{plugins.Ur_7V4x8.0.0.30-alpha.0.js → plugins.DIEspWyc.0.0.30-alpha.0.js} +1 -1
  33. package/dist/interface/sw.js +1 -1
  34. package/dist/interface/{system.CRn9Cy3C.0.0.30-alpha.0.js → system.23ZKsQ9W.0.0.30-alpha.0.js} +1 -1
  35. package/dist/interface/{ui.C-XbEPHk.0.0.30-alpha.0.js → ui.9t-Av5-L.0.0.30-alpha.0.js} +1 -1
  36. package/dist/interface/{v-calendar.CapJoEkJ.0.0.30-alpha.0.js → v-calendar.u5cKUC4g.0.0.30-alpha.0.js} +1 -1
  37. package/dist/interface/{vee-validate.DcgOEpak.0.0.30-alpha.0.js → vee-validate.DLoDjJ2f.0.0.30-alpha.0.js} +2 -2
  38. package/dist/interface/{vue-i18n.D2pqkMP3.0.0.30-alpha.0.js → vue-i18n.1W5jhc7l.0.0.30-alpha.0.js} +2 -2
  39. package/dist/interface/{vue-inline-svg.DorOTPIf.0.0.30-alpha.0.js → vue-inline-svg.Cc6uL4nI.0.0.30-alpha.0.js} +1 -1
  40. package/dist/interface/{vue-router.BVs4qaVs.0.0.30-alpha.0.js → vue-router.CgUDt9kL.0.0.30-alpha.0.js} +2 -2
  41. package/dist/interface/{vue3-apexcharts.CBV9x5vk.0.0.30-alpha.0.js → vue3-apexcharts.D8ONfVUq.0.0.30-alpha.0.js} +1 -1
  42. package/dist/interface/{vue3-dnd.CXMddGIZ.0.0.30-alpha.0.js → vue3-dnd.V9l5pvP2.0.0.30-alpha.0.js} +1 -1
  43. package/dist/interface/{vue3-toastify.DE7YmDSX.0.0.30-alpha.0.js → vue3-toastify.CjNgo6aC.0.0.30-alpha.0.js} +1 -1
  44. package/dist/interface/{vuetify.CYxxHEVN.0.0.30-alpha.0.js → vuetify.Box3fVLm.0.0.30-alpha.0.js} +1 -1
  45. package/dist/interface/zod.DnW1_kUe.0.0.30-alpha.0.js +1 -0
  46. package/dist/server/bin/cameraui.js +10 -7
  47. package/dist/server/bin/cameraui.js.map +1 -1
  48. package/dist/server/bin/fork.js +2 -2
  49. package/dist/server/bin/fork.js.map +1 -1
  50. package/dist/server/bin/installer/darwin.js +1 -1
  51. package/dist/server/bin/installer/darwin.js.map +1 -1
  52. package/dist/server/bin/installer/linux.js +1 -1
  53. package/dist/server/bin/installer/linux.js.map +1 -1
  54. package/dist/server/package.json +11 -15
  55. package/dist/server/src/api/controllers/backup.controller.js.map +1 -1
  56. package/dist/server/src/api/controllers/cameras.controller.js +1 -1
  57. package/dist/server/src/api/controllers/cameras.controller.js.map +1 -1
  58. package/dist/server/src/api/controllers/frameWorkers.controller.js +1 -1
  59. package/dist/server/src/api/controllers/frameWorkers.controller.js.map +1 -1
  60. package/dist/server/src/api/controllers/plugins.controller.js +1 -2
  61. package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
  62. package/dist/server/src/api/controllers/system.controller.js +1 -3
  63. package/dist/server/src/api/controllers/system.controller.js.map +1 -1
  64. package/dist/server/src/api/controllers/users.controller.js +2 -2
  65. package/dist/server/src/api/controllers/users.controller.js.map +1 -1
  66. package/dist/server/src/api/database/constants.d.ts +8 -0
  67. package/dist/server/src/api/database/constants.js +9 -0
  68. package/dist/server/src/api/database/constants.js.map +1 -0
  69. package/dist/server/src/api/database/index.d.ts +0 -8
  70. package/dist/server/src/api/database/index.js +14 -21
  71. package/dist/server/src/api/database/index.js.map +1 -1
  72. package/dist/server/src/api/database/migration.js +5 -4
  73. package/dist/server/src/api/database/migration.js.map +1 -1
  74. package/dist/server/src/api/database/types.d.ts +6 -6
  75. package/dist/server/src/api/go2rtc/api/application.js +1 -1
  76. package/dist/server/src/api/go2rtc/api/application.js.map +1 -1
  77. package/dist/server/src/api/index.js.map +1 -1
  78. package/dist/server/src/api/plugins/logger.plugin.js.map +1 -1
  79. package/dist/server/src/api/schemas/cameras.schema.d.ts +71 -71
  80. package/dist/server/src/api/schemas/config.schema.d.ts +6 -6
  81. package/dist/server/src/api/schemas/go2rtc.schema.d.ts +98 -98
  82. package/dist/server/src/api/schemas/users.schema.d.ts +116 -116
  83. package/dist/server/src/api/services/auth.service.js +1 -1
  84. package/dist/server/src/api/services/auth.service.js.map +1 -1
  85. package/dist/server/src/api/services/backup.service.js.map +1 -1
  86. package/dist/server/src/api/services/cameras.service.js +1 -1
  87. package/dist/server/src/api/services/cameras.service.js.map +1 -1
  88. package/dist/server/src/api/services/system.service.js +1 -1
  89. package/dist/server/src/api/services/system.service.js.map +1 -1
  90. package/dist/server/src/api/services/users.service.js +1 -1
  91. package/dist/server/src/api/services/users.service.js.map +1 -1
  92. package/dist/server/src/api/types/index.d.ts +2 -2
  93. package/dist/server/src/api/types/index.js.map +1 -1
  94. package/dist/server/src/api/utils/cert.js.map +1 -1
  95. package/dist/server/src/api/websocket/index.js.map +1 -1
  96. package/dist/server/src/api/websocket/nsp/notifications.js.map +1 -1
  97. package/dist/server/src/api/websocket/nsp/server.js +1 -1
  98. package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
  99. package/dist/server/src/api.d.ts +1 -7
  100. package/dist/server/src/api.js.map +1 -1
  101. package/dist/server/src/camera/controller.d.ts +4 -6
  102. package/dist/server/src/camera/controller.js +5 -8
  103. package/dist/server/src/camera/controller.js.map +1 -1
  104. package/dist/server/src/camera/types.d.ts +140 -13
  105. package/dist/server/src/camera/types.js.map +1 -1
  106. package/dist/server/src/decoder/index.js.map +1 -1
  107. package/dist/server/src/decoder/types.d.ts +1 -18
  108. package/dist/server/src/decoder/worker.d.ts +3 -2
  109. package/dist/server/src/decoder/worker.js +10 -22
  110. package/dist/server/src/decoder/worker.js.map +1 -1
  111. package/dist/server/src/go2rtc/index.js +2 -3
  112. package/dist/server/src/go2rtc/index.js.map +1 -1
  113. package/dist/server/src/main.js +3 -3
  114. package/dist/server/src/main.js.map +1 -1
  115. package/dist/server/src/nats/index.d.ts +3 -4
  116. package/dist/server/src/nats/index.js +1 -3
  117. package/dist/server/src/nats/index.js.map +1 -1
  118. package/dist/server/src/nats/proxy/cameraDevice.d.ts +2 -2
  119. package/dist/server/src/nats/proxy/coreManager.d.ts +4 -2
  120. package/dist/server/src/nats/proxy/coreManager.js +7 -0
  121. package/dist/server/src/nats/proxy/coreManager.js.map +1 -1
  122. package/dist/server/src/nats/proxy/deviceManager.d.ts +5 -5
  123. package/dist/server/src/nats/proxy/deviceManager.js +4 -2
  124. package/dist/server/src/nats/proxy/deviceManager.js.map +1 -1
  125. package/dist/server/src/nats/server.js +2 -3
  126. package/dist/server/src/nats/server.js.map +1 -1
  127. package/dist/server/src/nats/types.d.ts +19 -32
  128. package/dist/server/src/nats/utils.d.ts +1 -1
  129. package/dist/server/src/nats/websocket.js.map +1 -1
  130. package/dist/server/src/plugins/index.js +11 -9
  131. package/dist/server/src/plugins/index.js.map +1 -1
  132. package/dist/server/src/plugins/interfaces/base.d.ts +1 -1
  133. package/dist/server/src/plugins/plugin.d.ts +1 -1
  134. package/dist/server/src/plugins/plugin.js +4 -4
  135. package/dist/server/src/plugins/plugin.js.map +1 -1
  136. package/dist/server/src/plugins/types.d.ts +36 -3
  137. package/dist/server/src/plugins/types.js.map +1 -1
  138. package/dist/server/src/plugins/worker.js +7 -16
  139. package/dist/server/src/plugins/worker.js.map +1 -1
  140. package/dist/server/src/services/config/constants.js +2 -2
  141. package/dist/server/src/services/config/constants.js.map +1 -1
  142. package/dist/server/src/services/config/index.d.ts +2 -0
  143. package/dist/server/src/services/config/index.js +10 -2
  144. package/dist/server/src/services/config/index.js.map +1 -1
  145. package/dist/server/src/services/logger/index.d.ts +4 -16
  146. package/dist/server/src/services/logger/index.js +4 -83
  147. package/dist/server/src/services/logger/index.js.map +1 -1
  148. package/dist/server/src/services/logger/types.d.ts +8 -0
  149. package/dist/server/src/services/logger/types.js +2 -0
  150. package/dist/server/src/services/logger/types.js.map +1 -0
  151. package/localdeps.txt +4 -0
  152. package/package.json +11 -15
  153. package/dist/interface/@tanstack.DYQ7Jj8U.0.0.30-alpha.0.js +0 -1
  154. package/dist/interface/@vue.CvjRlaU6.0.0.30-alpha.0.js +0 -17
  155. package/dist/interface/Account.Dm2FouAU.0.0.30-alpha.0.js +0 -1
  156. package/dist/interface/Adminpanel.B2Aiobnt.0.0.30-alpha.0.js +0 -2
  157. package/dist/interface/Appearance.EfH0tMdM.0.0.30-alpha.0.js +0 -1
  158. package/dist/interface/Backup.D8SRclhB.0.0.30-alpha.0.js +0 -1
  159. package/dist/interface/Camera.D-cOf4sS.0.0.30-alpha.0.js +0 -1
  160. package/dist/interface/Cameras.oolxbmXU.0.0.30-alpha.0.js +0 -1
  161. package/dist/interface/Camview.BHw8Pe3v.0.0.30-alpha.0.js +0 -1
  162. package/dist/interface/Config.DmPTI5iz.0.0.30-alpha.0.js +0 -1
  163. package/dist/interface/Console.Dq4khHFF.0.0.30-alpha.0.js +0 -4
  164. package/dist/interface/FirstSteps.Bo467abx.0.0.30-alpha.0.js +0 -1
  165. package/dist/interface/Plugin.BLTVcTt1.0.0.30-alpha.0.js +0 -1
  166. package/dist/interface/Plugins.Cp8mK__o.0.0.30-alpha.0.js +0 -1
  167. package/dist/interface/Recordings.B_JIFk_G.0.0.30-alpha.0.js +0 -1
  168. package/dist/interface/Recordings.gMbvwmk_.0.0.30-alpha.0.js +0 -1
  169. package/dist/interface/Settings.EhHJjGrl.0.0.30-alpha.0.js +0 -1
  170. package/dist/interface/System.VodoK0U6.0.0.30-alpha.0.js +0 -4
  171. package/dist/interface/User.DpcN7DEC.0.0.30-alpha.0.js +0 -1
  172. package/dist/interface/VPlayer.DYua3Eh_.0.0.30-alpha.0.js +0 -1
  173. package/dist/interface/main.19pRAqPU.0.0.30-alpha.0.js +0 -4
  174. package/dist/interface/zod.BaF2dDCd.0.0.30-alpha.0.js +0 -1
  175. package/dist/server/src/camera/device.d.ts +0 -29
  176. package/dist/server/src/camera/device.js +0 -20
  177. package/dist/server/src/camera/device.js.map +0 -1
  178. package/dist/server/src/camera/index.d.ts +0 -90
  179. package/dist/server/src/camera/index.js +0 -581
  180. package/dist/server/src/camera/index.js.map +0 -1
  181. package/dist/server/src/camera/interfaces/camera.d.ts +0 -10
  182. package/dist/server/src/camera/interfaces/camera.js +0 -23
  183. package/dist/server/src/camera/interfaces/camera.js.map +0 -1
  184. package/dist/server/src/camera/interfaces/prebuffer.d.ts +0 -11
  185. package/dist/server/src/camera/interfaces/prebuffer.js +0 -31
  186. package/dist/server/src/camera/interfaces/prebuffer.js.map +0 -1
  187. package/dist/server/src/camera/interfaces/ptz.d.ts +0 -13
  188. package/dist/server/src/camera/interfaces/ptz.js +0 -47
  189. package/dist/server/src/camera/interfaces/ptz.js.map +0 -1
  190. package/dist/server/src/camera/iou.d.ts +0 -2
  191. package/dist/server/src/camera/iou.js +0 -53
  192. package/dist/server/src/camera/iou.js.map +0 -1
  193. package/dist/server/src/camera/polygon.d.ts +0 -6
  194. package/dist/server/src/camera/polygon.js +0 -151
  195. package/dist/server/src/camera/polygon.js.map +0 -1
  196. package/dist/server/src/camera/streaming/peer-connection.d.ts +0 -55
  197. package/dist/server/src/camera/streaming/peer-connection.js +0 -203
  198. package/dist/server/src/camera/streaming/peer-connection.js.map +0 -1
  199. package/dist/server/src/camera/streaming/webrtc-connection.d.ts +0 -35
  200. package/dist/server/src/camera/streaming/webrtc-connection.js +0 -170
  201. package/dist/server/src/camera/streaming/webrtc-connection.js.map +0 -1
  202. package/dist/server/src/camera/streaming/werift-session.d.ts +0 -41
  203. package/dist/server/src/camera/streaming/werift-session.js +0 -179
  204. package/dist/server/src/camera/streaming/werift-session.js.map +0 -1
  205. package/dist/server/src/camera/videoFrame.d.ts +0 -24
  206. package/dist/server/src/camera/videoFrame.js +0 -104
  207. package/dist/server/src/camera/videoFrame.js.map +0 -1
  208. package/dist/server/src/index.d.ts +0 -18
  209. package/dist/server/src/index.js +0 -5
  210. package/dist/server/src/index.js.map +0 -1
  211. package/dist/server/src/nats/connection.d.ts +0 -17
  212. package/dist/server/src/nats/connection.js +0 -66
  213. package/dist/server/src/nats/connection.js.map +0 -1
  214. package/dist/server/src/nats/constants.d.ts +0 -1
  215. package/dist/server/src/nats/constants.js +0 -2
  216. package/dist/server/src/nats/constants.js.map +0 -1
  217. package/dist/server/src/nats/error.d.ts +0 -9
  218. package/dist/server/src/nats/error.js +0 -11
  219. package/dist/server/src/nats/error.js.map +0 -1
  220. package/dist/server/src/nats/messageQueue.d.ts +0 -20
  221. package/dist/server/src/nats/messageQueue.js +0 -132
  222. package/dist/server/src/nats/messageQueue.js.map +0 -1
  223. package/dist/server/src/nats/subscription.d.ts +0 -12
  224. package/dist/server/src/nats/subscription.js +0 -21
  225. package/dist/server/src/nats/subscription.js.map +0 -1
  226. package/dist/server/src/polyglot/node/decoder/child.d.ts +0 -1
  227. package/dist/server/src/polyglot/node/decoder/child.js +0 -644
  228. package/dist/server/src/polyglot/node/decoder/child.js.map +0 -1
  229. package/dist/server/src/polyglot/node/decoder/imageUtils.d.ts +0 -24
  230. package/dist/server/src/polyglot/node/decoder/imageUtils.js +0 -205
  231. package/dist/server/src/polyglot/node/decoder/imageUtils.js.map +0 -1
  232. package/dist/server/src/polyglot/node/decoder/wasm/build/decoder.d.ts +0 -144
  233. package/dist/server/src/polyglot/node/decoder/wasm/build/decoder.js +0 -65
  234. package/dist/server/src/polyglot/node/decoder/wasm/build/decoder.wasm +0 -0
  235. package/dist/server/src/polyglot/node/decoder/wasm/test/imageUtils.py +0 -106
  236. package/dist/server/src/polyglot/node/plugins/cameraStorage.d.ts +0 -107
  237. package/dist/server/src/polyglot/node/plugins/cameraStorage.js +0 -292
  238. package/dist/server/src/polyglot/node/plugins/cameraStorage.js.map +0 -1
  239. package/dist/server/src/polyglot/node/plugins/child.d.ts +0 -28
  240. package/dist/server/src/polyglot/node/plugins/child.js +0 -222
  241. package/dist/server/src/polyglot/node/plugins/child.js.map +0 -1
  242. package/dist/server/src/polyglot/node/plugins/configService.d.ts +0 -104
  243. package/dist/server/src/polyglot/node/plugins/configService.js +0 -213
  244. package/dist/server/src/polyglot/node/plugins/configService.js.map +0 -1
  245. package/dist/server/src/polyglot/node/plugins/pluginApi.d.ts +0 -35
  246. package/dist/server/src/polyglot/node/plugins/pluginApi.js +0 -23
  247. package/dist/server/src/polyglot/node/plugins/pluginApi.js.map +0 -1
  248. package/dist/server/src/polyglot/node/plugins/pluginLogger.d.ts +0 -18
  249. package/dist/server/src/polyglot/node/plugins/pluginLogger.js +0 -89
  250. package/dist/server/src/polyglot/node/plugins/pluginLogger.js.map +0 -1
  251. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.d.ts +0 -69
  252. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js +0 -423
  253. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js.map +0 -1
  254. package/dist/server/src/polyglot/node/plugins/proxy/coreManager.d.ts +0 -40
  255. package/dist/server/src/polyglot/node/plugins/proxy/coreManager.js +0 -105
  256. package/dist/server/src/polyglot/node/plugins/proxy/coreManager.js.map +0 -1
  257. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.d.ts +0 -47
  258. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.js +0 -164
  259. package/dist/server/src/polyglot/node/plugins/proxy/deviceManager.js.map +0 -1
  260. package/dist/server/src/polyglot/node/plugins/schema.d.ts +0 -293
  261. package/dist/server/src/polyglot/node/plugins/schema.js +0 -740
  262. package/dist/server/src/polyglot/node/plugins/schema.js.map +0 -1
  263. package/dist/server/src/polyglot/node/plugins/storageController.d.ts +0 -30
  264. package/dist/server/src/polyglot/node/plugins/storageController.js +0 -40
  265. package/dist/server/src/polyglot/node/plugins/storageController.js.map +0 -1
  266. package/dist/server/src/polyglot/python/camera/interfaces/camera.py +0 -20
  267. package/dist/server/src/polyglot/python/camera/interfaces/prebuffer.py +0 -28
  268. package/dist/server/src/polyglot/python/camera/interfaces/ptz.py +0 -38
  269. package/dist/server/src/polyglot/python/camera/iou.py +0 -80
  270. package/dist/server/src/polyglot/python/camera/polygon.py +0 -173
  271. package/dist/server/src/polyglot/python/camera/video_frame.py +0 -188
  272. package/dist/server/src/polyglot/python/camera_ui_types/__init__.py +0 -1200
  273. package/dist/server/src/polyglot/python/decoder/child.py +0 -882
  274. package/dist/server/src/polyglot/python/decoder/image_utils.py +0 -149
  275. package/dist/server/src/polyglot/python/decoder/typings.py +0 -99
  276. package/dist/server/src/polyglot/python/plugins/camera_storage.py +0 -391
  277. package/dist/server/src/polyglot/python/plugins/child.py +0 -394
  278. package/dist/server/src/polyglot/python/plugins/config_service.py +0 -187
  279. package/dist/server/src/polyglot/python/plugins/constants.py +0 -1
  280. package/dist/server/src/polyglot/python/plugins/message_queue.py +0 -182
  281. package/dist/server/src/polyglot/python/plugins/plugin_api.py +0 -34
  282. package/dist/server/src/polyglot/python/plugins/plugin_logger.py +0 -111
  283. package/dist/server/src/polyglot/python/plugins/proxy/camera_device.py +0 -1599
  284. package/dist/server/src/polyglot/python/plugins/proxy/core_manager.py +0 -145
  285. package/dist/server/src/polyglot/python/plugins/proxy/device_manager.py +0 -226
  286. package/dist/server/src/polyglot/python/plugins/schema.py +0 -181
  287. package/dist/server/src/polyglot/python/plugins/storage_controller.py +0 -58
  288. package/dist/server/src/polyglot/python/plugins/typings.py +0 -253
  289. package/dist/server/src/polyglot/python/utilities/connection.py +0 -86
  290. package/dist/server/src/polyglot/python/utilities/json_lmdb.py +0 -19
  291. package/dist/server/src/polyglot/python/utilities/object_path.py +0 -195
  292. package/dist/server/src/polyglot/python/utilities/packer.py +0 -11
  293. package/dist/server/src/polyglot/python/utilities/subscriptions.py +0 -22
  294. package/dist/server/src/polyglot/python/utilities/task.py +0 -35
  295. package/dist/server/src/polyglot/python/utilities/thread.py +0 -10
  296. package/dist/server/src/polyglot/python/utilities/utils.py +0 -73
  297. package/dist/server/src/utils/ffmpeg.d.ts +0 -2
  298. package/dist/server/src/utils/ffmpeg.js +0 -61
  299. package/dist/server/src/utils/ffmpeg.js.map +0 -1
  300. package/dist/server/src/utils/network.d.ts +0 -12
  301. package/dist/server/src/utils/network.js +0 -92
  302. package/dist/server/src/utils/network.js.map +0 -1
  303. package/dist/server/src/utils/npm.d.ts +0 -4
  304. package/dist/server/src/utils/npm.js +0 -128
  305. package/dist/server/src/utils/npm.js.map +0 -1
  306. package/dist/server/src/utils/packer.d.ts +0 -2
  307. package/dist/server/src/utils/packer.js +0 -17
  308. package/dist/server/src/utils/packer.js.map +0 -1
  309. package/dist/server/src/utils/pythonInstaller.d.ts +0 -48
  310. package/dist/server/src/utils/pythonInstaller.js +0 -494
  311. package/dist/server/src/utils/pythonInstaller.js.map +0 -1
  312. package/dist/server/src/utils/reader.d.ts +0 -5
  313. package/dist/server/src/utils/reader.js +0 -41
  314. package/dist/server/src/utils/reader.js.map +0 -1
  315. package/dist/server/src/utils/subscribed.d.ts +0 -9
  316. package/dist/server/src/utils/subscribed.js +0 -17
  317. package/dist/server/src/utils/subscribed.js.map +0 -1
  318. package/dist/server/src/utils/utils.d.ts +0 -10
  319. package/dist/server/src/utils/utils.js +0 -165
  320. package/dist/server/src/utils/utils.js.map +0 -1
@@ -1,882 +0,0 @@
1
- import asyncio
2
- import os
3
- import random
4
- import re
5
- import signal
6
- import string
7
- import traceback
8
- from contextlib import suppress
9
- from datetime import datetime
10
- from typing import Any, Literal, Optional, Union, cast
11
-
12
- from camera_ui_types import DecoderFormat, FrameData, FrameMetadata, ImageOptions
13
- from image_utils import process_image_threaded
14
- from nats.aio.msg import Msg
15
- from nats.errors import NoRespondersError, TimeoutError
16
- from plugins.plugin_logger import PluginLogger
17
- from plugins.typings import ProcessPrebufferedImageRequest
18
- from typings import (
19
- BaseFrameWorkerConfig,
20
- FfmpegArgs,
21
- FfmpegArgsResponse,
22
- FrameWorkerClient,
23
- MainToWorkerMessage,
24
- MainToWorkerResponse,
25
- PingMessage,
26
- PongMessage,
27
- PrebufferContainer,
28
- RecoveredClient,
29
- SignalingRequest,
30
- SignalingResponse,
31
- WorkerMessage,
32
- WorkerToMainMessage,
33
- WorkerToMainResponse,
34
- )
35
- from utilities.connection import ProxyConnection
36
- from utilities.packer import pack, unpack
37
- from utilities.subscriptions import ProxySubscription
38
- from utilities.task import TaskSet
39
-
40
- PIX_FMT: Literal["yuv420p", "rgb24"] = "yuv420p"
41
-
42
-
43
- class FrameWorkerChild:
44
- def __init__(self):
45
- self.config: BaseFrameWorkerConfig = {
46
- "frameWorkerId": os.environ["FRAME_WORKER_ID"],
47
- "cameraId": os.environ["CAMERA_ID"],
48
- "cameraName": os.environ["CAMERA_NAME"],
49
- "ffmpegPath": os.environ["FFMPEG_PATH"],
50
- "logLevel": os.environ.get("LOGGER_LEVEL") or "debug",
51
- "disableTimestamps": os.environ.get("LOGGER_DISABLE_TIMESTAMPS") == "true",
52
- "auth": {
53
- "user": os.environ["PROXY_USER"],
54
- "password": os.environ["PROXY_PASSWORD"],
55
- },
56
- "proxyEndpoints": [endpoint for endpoint in os.environ["PROXY_ENDPOINTS"].split(",")],
57
- "fps": int(os.environ["FPS"]) if "FPS" in os.environ else 10,
58
- "resolution": int(os.environ["RESOLUTION"]) if "RESOLUTION" in os.environ else 640,
59
- }
60
-
61
- del os.environ["FRAME_WORKER_ID"]
62
- del os.environ["CAMERA_ID"]
63
- del os.environ["CAMERA_NAME"]
64
- del os.environ["FFMPEG_PATH"]
65
- del os.environ["PROXY_USER"]
66
- del os.environ["PROXY_PASSWORD"]
67
- del os.environ["PROXY_ENDPOINTS"]
68
- del os.environ["FPS"]
69
- del os.environ["RESOLUTION"]
70
-
71
- self.logger = PluginLogger("Frame Worker", self.config["disableTimestamps"])
72
- self.logger.debug_enabled = self.config["logLevel"] == "debug" or self.config["logLevel"] == "trace"
73
- self.logger.trace_enabled = self.config["logLevel"] == "trace"
74
-
75
- self.subject = f"{self.config['frameWorkerId']}.frameWorkerChild"
76
- self.ipc_subject = f"{self.config['frameWorkerId']}.frameWorker"
77
- self.image_subject = f"{self.config['cameraId']}.frameGenerator"
78
- self.signaling_subject = f"{self.config['cameraId']}.frameGenerator.signaling"
79
- self.frame_subject = f"{self.config['cameraId']}.frameReceiver"
80
-
81
- self.publisher: Optional[ProxyConnection] = None
82
- self.signaling_publisher: Optional[ProxyConnection] = None
83
- self.image_publisher: Optional[ProxyConnection] = None
84
- self.frame_publisher: Optional[ProxyConnection] = None
85
-
86
- self.subscriber: Optional[ProxySubscription] = None
87
- self.image_subscriber: Optional[ProxySubscription] = None
88
- self.signal_subscriber: Optional[ProxySubscription] = None
89
-
90
- self.clients: dict[str, FrameWorkerClient] = {}
91
- self.tasks: TaskSet = TaskSet(f"{self.config['cameraName']} FrameWorkerChild")
92
-
93
- self.prebuffer_full = False
94
- self.prebuffer_duration = 7
95
- self.prebuffer_container: list[PrebufferContainer] = []
96
-
97
- self.shutdown_event = asyncio.Event()
98
- self.loop = asyncio.get_event_loop()
99
-
100
- self._metadata: Optional[FrameMetadata] = None
101
- self.metadata_event = asyncio.Event()
102
- self.ffmpeg_process = None
103
-
104
- self.stderr_task: Optional[asyncio.Task[None]] = None
105
- self.stdout_task: Optional[asyncio.Task[None]] = None
106
- self.close_task: Optional[asyncio.Task[None]] = None
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
-
115
- self.restart_timeout: Optional[asyncio.TimerHandle] = None
116
- self.restart_watchdog_delay = 5
117
-
118
- self.close_timeout: Optional[asyncio.TimerHandle] = None
119
- self.close_delay = 5
120
-
121
- @property
122
- def log_prefix(self) -> str:
123
- return f"{self.config['cameraName']}:"
124
-
125
- @property
126
- async def metadata(self) -> FrameMetadata:
127
- if not self._metadata:
128
- await self.metadata_event.wait()
129
-
130
- return cast(FrameMetadata, self._metadata)
131
-
132
- async def listen(self) -> None:
133
- self.setup_signal_handlers()
134
-
135
- servers = [endpoint for endpoint in self.config["proxyEndpoints"] if endpoint.startswith("nats://")]
136
-
137
- self.publisher = ProxyConnection(self.subject, servers, self.config["auth"])
138
- await self.publisher.connect()
139
-
140
- self.image_publisher = ProxyConnection(self.image_subject, servers, self.config["auth"])
141
- await self.image_publisher.connect()
142
-
143
- self.frame_publisher = ProxyConnection(self.frame_subject, servers, self.config["auth"])
144
- await self.frame_publisher.connect()
145
-
146
- self.signaling_publisher = ProxyConnection(self.signaling_subject, servers, self.config["auth"])
147
- await self.signaling_publisher.connect()
148
-
149
- self.subscriber = await self.publisher.subscribe(subject=self.subject)
150
- self.subscriber.on("message", self.on_worker_message)
151
-
152
- self.image_subscriber = await self.image_publisher.subscribe(subject=self.image_subject)
153
- self.image_subscriber.on("message", self.on_image_request)
154
-
155
- self.signal_subscriber = await self.signaling_publisher.subscribe(subject=self.signaling_subject)
156
- self.signal_subscriber.on("message", self.on_signaling_message)
157
-
158
- await self.send("started")
159
- await self.shutdown_event.wait()
160
-
161
- async def close(self) -> None:
162
- self.closed = True
163
- # await self.stop()
164
-
165
- with suppress(Exception):
166
- if self.publisher:
167
- await self.publisher.close()
168
-
169
- if self.image_publisher:
170
- await self.image_publisher.close()
171
-
172
- if self.signaling_publisher:
173
- await self.signaling_publisher.close()
174
-
175
- if self.frame_publisher:
176
- await self.frame_publisher.close()
177
-
178
- self.shutdown_event.set()
179
-
180
- async def start(self) -> None:
181
- try:
182
- self.closed = False
183
- self.restarting = False
184
- self.stopped = False
185
-
186
- if self.close_timeout:
187
- self.close_timeout.cancel()
188
- self.close_timeout = None
189
-
190
- await self.start_video_session()
191
- except Exception as e:
192
- self.logger.error(f"{self.log_prefix} An error occurred while starting frame session:", e)
193
- await self.restart_ffmpeg()
194
-
195
- async def stop(self) -> None:
196
- self.stopped = True
197
- await self.disconnect_all_clients()
198
- await self.reset()
199
-
200
- async def reset(self) -> None:
201
- self.reset_prebuffer()
202
- self.reset_metadata()
203
- await self.reset_video_session()
204
-
205
- def reset_prebuffer(self) -> None:
206
- self.prebuffer_full = False
207
- self.prebuffer_container = []
208
-
209
- def reset_metadata(self) -> None:
210
- self.metadata_event.clear()
211
- self._metadata = None
212
-
213
- async def reset_video_session(self) -> None:
214
- if self.close_timeout:
215
- self.close_timeout.cancel()
216
- self.close_timeout = None
217
-
218
- if self.restart_timeout:
219
- self.restart_timeout.cancel()
220
- self.restart_timeout = None
221
-
222
- if self.stderr_task:
223
- self.stderr_task.cancel()
224
- self.stderr_task = None
225
-
226
- if self.stdout_task:
227
- self.stdout_task.cancel()
228
- self.stdout_task = None
229
-
230
- if self.close_task:
231
- self.close_task.cancel()
232
- self.close_task = None
233
-
234
- if self.ffmpeg_process:
235
- self.logger.debug(f"{self.log_prefix} Stopping FFmpeg process...")
236
- self.ffmpeg_process.kill()
237
- await self.ffmpeg_process.wait()
238
- self.ffmpeg_process = None
239
-
240
- async def start_video_session(self) -> None:
241
- self.reset_prebuffer()
242
- await self.reset_video_session()
243
-
244
- # await self.reset()
245
- self.stopped = False
246
-
247
- try:
248
- self.tasks.add(self.spawn_ffmpeg())
249
- await asyncio.wait_for(self.metadata_event.wait(), timeout=15)
250
- self.logger.debug(f"{self.log_prefix} Frame session started")
251
- except asyncio.TimeoutError:
252
- raise Exception("FFmpeg process timed out") from None
253
-
254
- def build_ffmpeg_args(self, ffmpeg_path: str, camera_source: str, ffmpeg_args: FfmpegArgs) -> list[str]:
255
- return [
256
- ffmpeg_path,
257
- "-hide_banner",
258
- # "-vsync",
259
- # "0",
260
- "-hwaccel",
261
- ffmpeg_args["hwaccel"],
262
- *ffmpeg_args["hwaccelArgs"],
263
- "-fflags",
264
- "nobuffer",
265
- "-flags",
266
- "low_delay",
267
- "-analyzeduration",
268
- "0",
269
- "-probesize",
270
- "500000",
271
- "-reorder_queue_size",
272
- "0",
273
- "-user_agent",
274
- "camera.ui/decoder",
275
- "-rtsp_transport",
276
- "tcp",
277
- "-i",
278
- camera_source,
279
- "-threads",
280
- ffmpeg_args["threads"],
281
- "-vf",
282
- f"showinfo=checksum=0,{ffmpeg_args['hwaccelFilter'].replace('640', str(self.config['resolution']))},fps=fps={self.config['fps']}",
283
- "-an",
284
- "-dn",
285
- "-sn",
286
- "-vcodec",
287
- "rawvideo",
288
- "-f",
289
- "rawvideo",
290
- "-pix_fmt",
291
- PIX_FMT,
292
- "-",
293
- ]
294
-
295
- async def spawn_ffmpeg(self) -> None:
296
- response = cast(Union[FfmpegArgsResponse, None], await self.send("getFFmpegArgs"))
297
-
298
- if not response:
299
- raise Exception("Error getting FFmpeg args from main process")
300
-
301
- args = self.build_ffmpeg_args(
302
- self.config["ffmpegPath"], response["cameraSource"], response["ffmpegArgs"]
303
- )
304
-
305
- self.logger.debug(f"{self.log_prefix} Starting FFmpeg process: {' '.join(args)}")
306
-
307
- self.ffmpeg_process = await asyncio.create_subprocess_exec(
308
- *args,
309
- stdout=asyncio.subprocess.PIPE,
310
- stderr=asyncio.subprocess.PIPE,
311
- limit=10**8,
312
- )
313
-
314
- self.stderr_task = self.tasks.add(self.handle_ffmpeg_stderr())
315
- self.stdout_task = self.tasks.add(self.handle_ffmpeg_stdout())
316
- self.close_task = self.tasks.add(self.handle_ffmpeg_close())
317
-
318
- async def restart_ffmpeg(self) -> None:
319
- if not self.stopped and not self.closed and not self.restarting and len(self.clients) > 0:
320
- self.restarting = True
321
-
322
- self.logger.log(f"{self.log_prefix} Restarting FFmpeg process in {self.restart_delay} seconds...")
323
-
324
- with suppress(asyncio.TimeoutError):
325
- await asyncio.wait_for(asyncio.sleep(self.restart_delay), timeout=self.restart_delay)
326
-
327
- if not self.stopped and not self.closed and len(self.clients) > 0:
328
- await self.start()
329
- else:
330
- self.restarting = False
331
-
332
- async def handle_ffmpeg_stderr(self) -> None:
333
- try:
334
-
335
- def is_closed() -> bool:
336
- return (
337
- self.stopped
338
- or self.closed
339
- or not self.ffmpeg_process
340
- or not self.ffmpeg_process.stderr
341
- or self.ffmpeg_process.returncode is not None
342
- )
343
-
344
- while not is_closed():
345
- try:
346
- line = await self.ffmpeg_process.stderr.readline() # type: ignore
347
-
348
- if is_closed():
349
- break
350
-
351
- if not line or len(line) == 0:
352
- continue
353
-
354
- line = line.decode("utf-8").strip()
355
- metadata = self.parse_frame_info(line)
356
-
357
- if metadata:
358
- self.logger.debug(f"{self.log_prefix} Metadata:", metadata)
359
- self._metadata = metadata
360
- self.metadata_event.set()
361
- break
362
-
363
- except Exception as e:
364
- self.logger.error(
365
- f"{self.log_prefix} An error occurred while reading ffmpeg stderr:",
366
- e,
367
- )
368
-
369
- break
370
- except asyncio.CancelledError:
371
- pass
372
-
373
- async def handle_ffmpeg_stdout(self) -> None:
374
- try:
375
- metadata = (await self.metadata).copy()
376
- warned = False
377
-
378
- def is_closed() -> bool:
379
- return (
380
- self.stopped
381
- or self.closed
382
- or not self.frame_publisher
383
- or not self.ffmpeg_process
384
- or not self.ffmpeg_process.stdout
385
- or self.ffmpeg_process.returncode is not None
386
- )
387
-
388
- def reset_restart_watchdog():
389
- if self.restart_timeout:
390
- self.restart_timeout.cancel()
391
- self.restart_timeout = None
392
-
393
- def restart_watchdog():
394
- reset_restart_watchdog()
395
- self.restart_timeout = asyncio.get_event_loop().call_later(
396
- self.restart_watchdog_delay,
397
- lambda: asyncio.create_task(restart_ffmpeg(), name="FrameWorker Restart FFmpeg"),
398
- )
399
-
400
- async def restart_ffmpeg():
401
- if not self.stopped and not self.closed and not self.restarting:
402
- message = "FFmpeg process is not producing frames, terminating..."
403
-
404
- nonlocal warned
405
- if warned is False:
406
- self.logger.debug(f"{self.log_prefix} {message}")
407
- warned = True
408
- else:
409
- self.logger.error(f"{self.log_prefix} {message}")
410
-
411
- await self.reset()
412
- await self.restart_ffmpeg()
413
-
414
- while not is_closed():
415
- try:
416
- restart_watchdog()
417
-
418
- frame = await self.ffmpeg_process.stdout.readexactly( # type: ignore
419
- int(metadata["frameSize"]),
420
- )
421
-
422
- warned = False
423
-
424
- if is_closed():
425
- reset_restart_watchdog()
426
- break
427
-
428
- frame_id = self.generate_id()
429
- now = int(datetime.now().timestamp() * 1000)
430
-
431
- self.prebuffer_container.append(
432
- {
433
- "frame_id": frame_id,
434
- "time": now,
435
- "frame": frame,
436
- }
437
- )
438
-
439
- if (
440
- len(self.prebuffer_container) > 0
441
- and now - self.prebuffer_container[0]["time"] >= (self.prebuffer_duration * 1000)
442
- and not self.prebuffer_full
443
- ):
444
- self.logger.debug(f"{self.log_prefix} Prebuffer is full")
445
- self.prebuffer_full = True
446
-
447
- while len(self.prebuffer_container) > 0 and self.prebuffer_container[0]["time"] < now - (
448
- self.prebuffer_duration * 1000
449
- ):
450
- self.prebuffer_container.pop(0)
451
-
452
- if self.prebuffer_full:
453
- frame_data: FrameData = {"frameId": frame_id, "timestamp": now}
454
-
455
- await self.frame_publisher.publish(self.frame_subject, frame_data) # type: ignore
456
-
457
- except asyncio.IncompleteReadError as e:
458
- if not is_closed() and not self.restarting:
459
- self.logger.error(f"{self.log_prefix} Incomplete read error:", e)
460
- await asyncio.sleep(0.1)
461
-
462
- except Exception as e:
463
- if not is_closed() and not self.restarting:
464
- self.logger.error(
465
- f"{self.log_prefix} An error occurred while reading ffmpeg stdout:",
466
- e,
467
- )
468
- break
469
- finally:
470
- reset_restart_watchdog()
471
- except asyncio.CancelledError:
472
- pass
473
-
474
- async def handle_ffmpeg_close(self) -> None:
475
- try:
476
- if self.ffmpeg_process:
477
- code = await self.ffmpeg_process.wait()
478
-
479
- if code != 0 and code != -9 or not self.stopped:
480
- self.logger.error(
481
- self.log_prefix,
482
- f"FFmpeg process has exited with code {code} (unexpected)",
483
- )
484
- else:
485
- self.logger.debug(
486
- f"{self.log_prefix} FFmpeg process has exited with code {code} (expected)"
487
- )
488
-
489
- self.ffmpeg_process = None
490
-
491
- await self.reset()
492
- await self.restart_ffmpeg()
493
- except asyncio.CancelledError:
494
- pass
495
-
496
- def parse_frame_info(self, stderr: str) -> Optional[FrameMetadata]:
497
- patterns = {
498
- "s": r"s:\s*(\d+x\d+)",
499
- }
500
-
501
- matches: dict[str, str] = {}
502
-
503
- for key, pattern in patterns.items():
504
- match = re.search(pattern, stderr)
505
- if match:
506
- matches[key] = match.group(1)
507
-
508
- if "s" not in matches:
509
- return None
510
-
511
- width, height = map(int, matches["s"].split("x"))
512
- new_width = self.config["resolution"]
513
- new_height = round((new_width * height) / width)
514
- format: DecoderFormat = "yuv" if PIX_FMT == "yuv420p" else "rgb"
515
- bytes_per_pixel = 1.5 if format == "yuv" else 3
516
- frame_size = new_width * new_height * bytes_per_pixel
517
-
518
- metadata: FrameMetadata = {
519
- "format": format,
520
- "frameSize": frame_size,
521
- "width": new_width,
522
- "height": new_height,
523
- "origHeight": height,
524
- "origWidth": width,
525
- }
526
-
527
- return metadata
528
-
529
- async def send(
530
- self,
531
- message: WorkerMessage,
532
- data: Optional[dict[str, Any]] = None,
533
- ) -> Union[dict[str, Any], None]:
534
- if self.publisher is None:
535
- raise Exception("Publisher not initialized")
536
-
537
- if data is None:
538
- data = {}
539
-
540
- worker_message: WorkerToMainMessage = {
541
- "message": message,
542
- "data": data,
543
- }
544
-
545
- response: MainToWorkerResponse = await self.publisher.request(
546
- self.ipc_subject, worker_message, timeout=30
547
- )
548
-
549
- return response.get("response")
550
-
551
- async def add_client(self, plugin_id: str, request_id: str) -> None:
552
- if not self.clients.get(plugin_id):
553
- self.clients[plugin_id] = FrameWorkerClient(plugin_id, [request_id])
554
-
555
- client = self.clients[plugin_id]
556
-
557
- if request_id not in client.request_ids:
558
- client.request_ids.append(request_id)
559
-
560
- self.logger.trace(f"{self.log_prefix} New client {self.client_identifier(plugin_id, request_id)}")
561
-
562
- if self.close_timeout:
563
- self.close_timeout.cancel()
564
- self.close_timeout = None
565
-
566
- await self.send("updateClient", {"pluginId": client.plugin_id, "requestIds": client.request_ids})
567
-
568
- if len(self.clients) > 0 and self.ffmpeg_process is None and not self.restarting:
569
- await self.start()
570
-
571
- async def remove_client(self, plugin_id: str, request_id: str) -> None:
572
- client = self.clients[plugin_id]
573
-
574
- if not client or request_id not in client.request_ids:
575
- return
576
-
577
- self.logger.trace(
578
- f"{self.log_prefix} Removing client {self.client_identifier(plugin_id, request_id)}"
579
- )
580
-
581
- if request_id in client.request_ids:
582
- client.request_ids.remove(request_id)
583
-
584
- if len(client.request_ids) == 0:
585
- del self.clients[plugin_id]
586
- await self.send("removeClient", {"pluginId": client.plugin_id, "requestIds": client.request_ids})
587
- else:
588
- await self.send("updateClient", {"pluginId": client.plugin_id, "requestIds": client.request_ids})
589
-
590
- if len(self.clients) == 0:
591
- if self.close_timeout:
592
- self.close_timeout.cancel()
593
-
594
- self.close_timeout = asyncio.get_event_loop().call_later(
595
- self.close_delay, lambda: asyncio.create_task(self.stop(), name="FrameWorker Stop")
596
- )
597
-
598
- async def recover_clients(self, clients: list[RecoveredClient]) -> None:
599
- for client in clients:
600
- for request_id in client["requestIds"]:
601
- self.logger.trace(
602
- f"{self.log_prefix} Recovering client {self.client_identifier(client['pluginId'], request_id)}"
603
- )
604
-
605
- self.clients[client["pluginId"]] = FrameWorkerClient(client["pluginId"], client["requestIds"])
606
-
607
- if len(self.clients) > 0:
608
- await self.start()
609
-
610
- async def disconnect_client(self, camera_id: str, plugin_id: str, request_id: str) -> None:
611
- client = self.clients.get(plugin_id)
612
-
613
- if not self.signaling_publisher or not client or request_id not in client.request_ids:
614
- return
615
-
616
- ping_target_subject = f"{plugin_id}.camera.{camera_id}.signaling"
617
-
618
- response: SignalingResponse = {"type": "disconnect", "requestId": request_id}
619
-
620
- await self.signaling_publisher.publish(ping_target_subject, response)
621
- await self.remove_client(plugin_id, request_id)
622
-
623
- async def disconnect_all_clients(self) -> None:
624
- for client in self.clients.values():
625
- for request_id in client.request_ids:
626
- await self.disconnect_client(self.config["cameraId"], client.plugin_id, request_id)
627
-
628
- async def handle_connect_message(self, message: SignalingRequest) -> None:
629
- await self.add_client(message["pluginId"], message["requestId"])
630
- self.tasks.add(self.start_ping_pong(message["cameraId"], message["pluginId"], message["requestId"]))
631
-
632
- async def handle_disconnect_message(self, message: SignalingRequest) -> None:
633
- await self.remove_client(message["pluginId"], message["requestId"])
634
-
635
- async def handle_metadata_message(self, msg: Msg, message: SignalingRequest) -> None:
636
- metadata = (await self.metadata).copy()
637
-
638
- response: SignalingResponse = {
639
- "type": "metadata",
640
- "requestId": message["requestId"],
641
- "metadata": metadata,
642
- }
643
-
644
- await msg.respond(pack(response))
645
-
646
- async def start_ping_pong(self, camera_id: str, plugin_id: str, request_id: str) -> None:
647
- ping_target_subject = f"{plugin_id}.camera.{camera_id}.signaling"
648
- ping_interval = 3
649
- ping_timeout = 2
650
-
651
- def request_id_exists() -> bool:
652
- return self.clients.get(plugin_id, False) and request_id in self.clients[plugin_id].request_ids
653
-
654
- while self.signaling_publisher and request_id_exists():
655
- try:
656
- message: PingMessage = {"type": "ping", "requestId": request_id}
657
-
658
- # self.logger.trace(f"{self.log_prefix} Sending ping to {self.client_identifier(plugin_id, request_id)}")
659
-
660
- response: PongMessage = await self.signaling_publisher.request(
661
- ping_target_subject,
662
- message,
663
- timeout=ping_timeout,
664
- )
665
-
666
- if not request_id_exists():
667
- break
668
-
669
- if response["type"] != "pong":
670
- raise Exception("Invalid response to ping")
671
-
672
- # self.logger.trace(f"{self.log_prefix} Received pong from {self.client_identifier(plugin_id, request_id)}")
673
-
674
- await asyncio.sleep(ping_interval)
675
- except (Exception, NoRespondersError, TimeoutError) as e:
676
- if request_id_exists():
677
- if isinstance(e, (NoRespondersError, TimeoutError)):
678
- # self.logger.trace(f"{self.log_prefix} Ping/Pong timeout for {self.client_identifier(plugin_id, request_id)}")
679
- await self.remove_client(plugin_id, request_id)
680
- else:
681
- self.logger.error(f"{self.log_prefix} Error during ping/pong:", e)
682
-
683
- break
684
-
685
- async def on_worker_message(self, msg: Msg) -> None:
686
- if self.closed:
687
- return
688
-
689
- self.tasks.add(self.respond_on_ipc_message(msg))
690
-
691
- async def on_signaling_message(self, msg: Msg) -> None:
692
- if self.closed:
693
- return
694
-
695
- self.tasks.add(self.respond_on_signaling_message(msg))
696
-
697
- async def on_image_request(self, msg: Msg) -> None:
698
- if self.closed:
699
- return
700
-
701
- self.tasks.add(self.respond_on_prebuffered_image_message(msg))
702
-
703
- async def respond_on_ipc_message(self, msg: Msg) -> None:
704
- try:
705
- main_message: MainToWorkerMessage = unpack(msg.data)
706
- response: WorkerToMainResponse = {}
707
- message = main_message["message"]
708
-
709
- if message == "setCameraName":
710
- self.config["cameraName"] = main_message["data"]["cameraName"]
711
- elif message == "recoverClients":
712
- await self.recover_clients(main_message["data"]["clients"])
713
-
714
- await msg.respond(pack(response))
715
- except Exception as e:
716
- self.logger.error(f"{self.log_prefix} Error while processing IPC respond:", e)
717
-
718
- async def respond_on_signaling_message(self, msg: Msg) -> None:
719
- try:
720
- message: SignalingRequest = unpack(msg.data)
721
-
722
- if message["type"] == "connect":
723
- await self.handle_connect_message(message)
724
- elif message["type"] == "disconnect":
725
- await self.handle_disconnect_message(message)
726
- elif message["type"] == "metadata":
727
- await self.handle_metadata_message(msg, message)
728
- except Exception as e:
729
- self.logger.error(f"{self.log_prefix} Error while processing signaling respond:", e)
730
-
731
- async def respond_on_prebuffered_image_message(self, msg: Msg) -> None:
732
- try:
733
- message: ProcessPrebufferedImageRequest = unpack(msg.data)
734
- metadata = message["metadata"]
735
- frame_data = message["frameData"]
736
- options = message["options"]
737
- prebuffer_duration = message["prebufferDuration"]
738
- width = metadata["width"]
739
- height = metadata["height"]
740
- format = metadata["format"]
741
-
742
- target_frame = self.find_target_frame(frame_data, prebuffer_duration)
743
-
744
- if not target_frame:
745
- self.log_frame_not_found()
746
- else:
747
- processed_image = await self.process_frame(
748
- target_frame["frame"], width, height, format, options
749
- )
750
- await msg.respond(pack(processed_image))
751
- return
752
-
753
- await msg.respond(pack(b""))
754
- except Exception as e:
755
- self.logger.error(f"{self.log_prefix} Error while processing image respond:", e)
756
-
757
- def find_target_frame(
758
- self, frame_data: FrameData, prebuffer_duration: int
759
- ) -> Optional[PrebufferContainer]:
760
- target_index = next(
761
- (
762
- i
763
- for i, frame in enumerate(self.prebuffer_container)
764
- if frame["frame_id"] == frame_data["frameId"]
765
- ),
766
- -1,
767
- )
768
-
769
- if target_index == -1:
770
- return self.find_nearest_frame(frame_data)
771
-
772
- if prebuffer_duration > 0:
773
- target_time = self.prebuffer_container[target_index]["time"] - prebuffer_duration * 1000
774
- result = self.find_nearest_frame_by_time(target_time)
775
- if result is not None:
776
- return result
777
-
778
- return self.prebuffer_container[target_index]
779
-
780
- def find_nearest_frame(self, frame_data: FrameData) -> Optional[PrebufferContainer]:
781
- max_time_diff = self.max_diff_frame * (1000 / self.config["fps"])
782
-
783
- nearest_frame = None
784
- min_time_diff = float("inf")
785
-
786
- for frame in self.prebuffer_container:
787
- current_time_diff = abs(frame["time"] - frame_data["timestamp"])
788
-
789
- if current_time_diff <= max_time_diff and (
790
- nearest_frame is None or current_time_diff < min_time_diff
791
- ):
792
- nearest_frame = frame
793
- min_time_diff = current_time_diff
794
-
795
- return nearest_frame
796
-
797
- def find_nearest_frame_by_time(self, target_time: float) -> Optional[PrebufferContainer]:
798
- max_time_diff = (self.max_diff_frame / self.config["fps"]) * 1000
799
- nearest_frame = None
800
- min_diff = float("inf")
801
-
802
- for frame in self.prebuffer_container:
803
- if frame["time"] <= target_time:
804
- time_diff = abs(frame["time"] - target_time)
805
- if time_diff <= max_time_diff and time_diff < min_diff:
806
- nearest_frame = frame
807
- min_diff = time_diff
808
-
809
- return nearest_frame
810
-
811
- async def process_frame(
812
- self,
813
- frame: bytes,
814
- width: int,
815
- height: int,
816
- format: Union[DecoderFormat, Literal["rgba", "gray"]],
817
- options: ImageOptions,
818
- ) -> bytes:
819
- return await process_image_threaded(
820
- frame,
821
- width,
822
- height,
823
- format,
824
- options,
825
- )
826
-
827
- def log_frame_not_found(self):
828
- if len(self.prebuffer_container) > 0:
829
- self.logger.warn(f"{self.log_prefix} Frame not found in prebuffer")
830
- else:
831
- self.logger.warn(f"{self.log_prefix} Prebuffer is empty! FFmpeg is not producing frames?")
832
-
833
- def client_identifier(self, plugin_id: str, request_id: str) -> str:
834
- return f"{plugin_id}:{request_id}"
835
-
836
- def generate_id(self, length: int = 9) -> str:
837
- characters = string.ascii_letters + string.digits
838
- return "".join(random.choice(characters) for _ in range(length))
839
-
840
- def setup_signal_handlers(self) -> None:
841
- loop = asyncio.get_running_loop()
842
- loop.set_exception_handler(self.handle_exception)
843
-
844
- for signame in ("SIGINT", "SIGTERM"):
845
- loop.add_signal_handler(
846
- getattr(signal, signame),
847
- lambda signame=signame: self.gracefully_close(signame, getattr(signal, signame)),
848
- )
849
-
850
- def gracefully_close(self, signame: str, reason: Any = "unknown") -> None:
851
- if self.stopped:
852
- return
853
-
854
- self.logger.debug(
855
- f"{self.log_prefix} Frame Worker is gracefully closing from signal='{signame}' reason='{reason}'..."
856
- )
857
-
858
- self.tasks.add(self.close())
859
- self.shutdown_event.set()
860
-
861
- def handle_exception(self, loop: asyncio.AbstractEventLoop, context: Any) -> None:
862
- print(f"{self.log_prefix} Exception context:", context)
863
- traceback.print_exc()
864
-
865
- msg = context.get("message", "Unknown error")
866
- exception = context.get("exception")
867
-
868
- if exception:
869
- tb_str = "".join(traceback.format_exception(type(exception), exception, exception.__traceback__)) # type: ignore
870
-
871
- self.logger.error(f"{self.log_prefix} Caught exception: {msg}\n{tb_str}")
872
- else:
873
- self.logger.error(f"{self.log_prefix} Caught exception: {msg}")
874
-
875
- if loop.is_running():
876
- self.gracefully_close("uncaughtException", msg)
877
-
878
-
879
- if __name__ == "__main__":
880
- worker = FrameWorkerChild()
881
- asyncio.run(worker.listen())
882
- # asyncio.run(worker.listen(), debug=True)