@camera.ui/server 0.0.50-internal.0 → 0.0.50-internal.2

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 (390) hide show
  1. package/dist/interface/index.html +44 -44
  2. package/dist/interface/js/{@primevue.OhxoO3MD.0.0.50-internal.0.js → @primevue.BAIU_p3r.0.0.50-internal.2.js} +1 -1
  3. package/dist/interface/js/{@tanstack.D1IL6eOv.0.0.50-internal.0.js → @tanstack.mdCorqzE.0.0.50-internal.2.js} +1 -1
  4. package/dist/interface/js/{@vee-validate.BGPyWWTh.0.0.50-internal.0.js → @vee-validate.D0vsSsly.0.0.50-internal.2.js} +1 -1
  5. package/dist/interface/js/{@vueuse.BsKtqdiG.0.0.50-internal.0.js → @vueuse.we5ZpWr2.0.0.50-internal.2.js} +1 -1
  6. package/dist/interface/js/{Adminpanel.D93Kvbwp.0.0.50-internal.0.js → Adminpanel.9AEBJ3H1.0.0.50-internal.2.js} +1 -1
  7. package/dist/interface/js/{Camera.DF2l3OMA.0.0.50-internal.0.js → Camera.CZcnEH0C.0.0.50-internal.2.js} +1 -1
  8. package/dist/interface/js/{Cameras.YzvZccdv.0.0.50-internal.0.js → Cameras.DE0LkcGu.0.0.50-internal.2.js} +2 -2
  9. package/dist/interface/js/Cammesh.CVvmkelB.0.0.50-internal.2.js +1 -0
  10. package/dist/interface/js/{Camview.CB7_HnoQ.0.0.50-internal.0.js → Camview.CUr4pGoU.0.0.50-internal.2.js} +1 -1
  11. package/dist/interface/js/{Config.BwflsWTm.0.0.50-internal.0.js → Config.CaJhu4fF.0.0.50-internal.2.js} +2 -2
  12. package/dist/interface/js/{Console.DZM8pN8Q.0.0.50-internal.0.js → Console.5VUmgryj.0.0.50-internal.2.js} +1 -1
  13. package/dist/interface/js/{CuiConsole.BXXU7Gth.0.0.50-internal.0.js → CuiConsole.iYmUxAR5.0.0.50-internal.2.js} +1 -1
  14. package/dist/interface/js/{CuiPluginCard.YFOrpbVD.0.0.50-internal.0.js → CuiPluginCard.DXPyE5mF.0.0.50-internal.2.js} +1 -1
  15. package/dist/interface/js/{CuiTimeline.VV5KSJd5.0.0.50-internal.0.js → CuiTimeline.BqtlOFnV.0.0.50-internal.2.js} +1 -1
  16. package/dist/interface/js/{FirstSteps.b-nvymgb.0.0.50-internal.0.js → FirstSteps.C_RI-SDQ.0.0.50-internal.2.js} +1 -1
  17. package/dist/interface/js/{Plugin.BOMpVEwM.0.0.50-internal.0.js → Plugin.Ch_v02eN.0.0.50-internal.2.js} +1 -1
  18. package/dist/interface/js/{Plugins.DdPwaUDA.0.0.50-internal.0.js → Plugins.BpXAYgSG.0.0.50-internal.2.js} +1 -1
  19. package/dist/interface/js/Recordings.CIKHJytH.0.0.50-internal.2.js +1 -0
  20. package/dist/interface/js/{Settings.D24ttaPu.0.0.50-internal.0.js → Settings.BcLoGNEy.0.0.50-internal.2.js} +1 -1
  21. package/dist/interface/js/{SettingsAccount.ms3aqz4E.0.0.50-internal.0.js → SettingsAccount.DMROW8kx.0.0.50-internal.2.js} +1 -1
  22. package/dist/interface/js/SettingsAppearance.CUaHpihp.0.0.50-internal.2.js +1 -0
  23. package/dist/interface/js/{SettingsAppearance.vue_vue_type_script_setup_true_lang.CBQ6lL3T.0.0.50-internal.0.js → SettingsAppearance.vue_vue_type_script_setup_true_lang.BuKZ-Uco.0.0.50-internal.2.js} +1 -1
  24. package/dist/interface/js/{SettingsBackup.BPEc4Rjg.0.0.50-internal.0.js → SettingsBackup.0QdwFSpv.0.0.50-internal.2.js} +1 -1
  25. package/dist/interface/js/SettingsRecordings.B-aj0U8_.0.0.50-internal.2.js +1 -0
  26. package/dist/interface/js/{SettingsRemote.BL5nXbUT.0.0.50-internal.0.js → SettingsRemote.Bi1YAJ76.0.0.50-internal.2.js} +1 -1
  27. package/dist/interface/js/{SettingsSystem.D7T0AIpm.0.0.50-internal.0.js → SettingsSystem.CAx66Iir.0.0.50-internal.2.js} +1 -1
  28. package/dist/interface/js/{SettingsUsers.1u0vCOVG.0.0.50-internal.0.js → SettingsUsers.CQcDtGy1.0.0.50-internal.2.js} +1 -1
  29. package/dist/interface/js/{VersionsHandler.vue_vue_type_script_setup_true_lang.xDishkkS.0.0.50-internal.0.js → VersionsHandler.vue_vue_type_script_setup_true_lang.BhXMzIhV.0.0.50-internal.2.js} +1 -1
  30. package/dist/interface/js/{ace-builds.D1nmvCwb.0.0.50-internal.0.js → ace-builds.C0sCXSu5.0.0.50-internal.2.js} +2 -2
  31. package/dist/interface/js/{ace.config.G96T2z54.0.0.50-internal.0.js → ace.config.DNqPWa0Q.0.0.50-internal.2.js} +1 -1
  32. package/dist/interface/js/{backup.B_Gzxtp_.0.0.50-internal.0.js → backup.DAXgG3bP.0.0.50-internal.2.js} +1 -1
  33. package/dist/interface/js/{chart.js.CP_nMnnf.0.0.50-internal.0.js → chart.js.Bq3YocRX.0.0.50-internal.2.js} +1 -1
  34. package/dist/interface/js/{close-circle.DYLuVIOg.0.0.50-internal.0.js → close-circle.9rxyNw0z.0.0.50-internal.2.js} +1 -1
  35. package/dist/interface/js/{dnd-core.T9OeW7mu.0.0.50-internal.0.js → dnd-core.BuEvHAsS.0.0.50-internal.2.js} +1 -1
  36. package/dist/interface/js/{draggabilly.D3zGcBb3.0.0.50-internal.0.js → draggabilly.DUdE08x8.0.0.50-internal.2.js} +1 -1
  37. package/dist/interface/js/{engine.io-client.kITRnq-g.0.0.50-internal.0.js → engine.io-client.DWU6YI8o.0.0.50-internal.2.js} +1 -1
  38. package/dist/interface/js/{fast-deep-equal.Bpc9mIsj.0.0.50-internal.0.js → fast-deep-equal.BpPyC6E4.0.0.50-internal.2.js} +1 -1
  39. package/dist/interface/js/{highlight.js.BlSXU-Wb.0.0.50-internal.0.js → highlight.js.BoR2Rhdd.0.0.50-internal.2.js} +1 -1
  40. package/dist/interface/js/{linkify-it.C3N1lWsJ.0.0.50-internal.0.js → linkify-it.BXzPONHs.0.0.50-internal.2.js} +1 -1
  41. package/dist/interface/js/{main.D0YR3Jat.0.0.50-internal.0.js → main.DwcBYfWc.0.0.50-internal.2.js} +3 -3
  42. package/dist/interface/js/{markdown-it.Dj0dt5s2.0.0.50-internal.0.js → markdown-it.BUDVOjPt.0.0.50-internal.2.js} +1 -1
  43. package/dist/interface/js/{nprogress.BwQWDcGj.0.0.50-internal.0.js → nprogress.CsG-OBoI.0.0.50-internal.2.js} +1 -1
  44. package/dist/interface/js/{pinia.DRh7plEC.0.0.50-internal.0.js → pinia.CqEQao5R.0.0.50-internal.2.js} +1 -1
  45. package/dist/interface/js/{primevue.Cl58krXf.0.0.50-internal.0.js → primevue.D91sslRx.0.0.50-internal.2.js} +1 -1
  46. package/dist/interface/js/{qrcode.C1sDj6P8.0.0.50-internal.0.js → qrcode.SdSTPzZO.0.0.50-internal.2.js} +1 -1
  47. package/dist/interface/js/{react-dnd-touch-backend.BA4WhUJo.0.0.50-internal.0.js → react-dnd-touch-backend.BwroebQ4.0.0.50-internal.2.js} +1 -1
  48. package/dist/interface/js/{refresh-rounded.D_SmhhUf.0.0.50-internal.0.js → refresh-rounded.DxM48YSv.0.0.50-internal.2.js} +1 -1
  49. package/dist/interface/js/{rxjs.C-fTBwEO.0.0.50-internal.0.js → rxjs.Cmpbnzo4.0.0.50-internal.2.js} +1 -1
  50. package/dist/interface/js/{socket.io-client.DKrRxgjP.0.0.50-internal.0.js → socket.io-client.DF2YgZo1.0.0.50-internal.2.js} +1 -1
  51. package/dist/interface/js/{socket.io-parser.EXj3YDH2.0.0.50-internal.0.js → socket.io-parser.BUs2ineY.0.0.50-internal.2.js} +1 -1
  52. package/dist/interface/js/{system.CY8rGd-Q.0.0.50-internal.0.js → system.DmkT2LKx.0.0.50-internal.2.js} +1 -1
  53. package/dist/interface/js/{trash-fill.CMnCN3Oy.0.0.50-internal.0.js → trash-fill.dPzyZDVa.0.0.50-internal.2.js} +1 -1
  54. package/dist/interface/js/{unidragger.D0w6Tqtt.0.0.50-internal.0.js → unidragger.y2ZlFveS.0.0.50-internal.2.js} +1 -1
  55. package/dist/interface/js/{vee-validate.BeiObIH8.0.0.50-internal.0.js → vee-validate.Y-alCADW.0.0.50-internal.2.js} +1 -1
  56. package/dist/interface/js/{vue-chartjs.DtdZdiYw.0.0.50-internal.0.js → vue-chartjs.sRVFTqDf.0.0.50-internal.2.js} +1 -1
  57. package/dist/interface/js/{vue-i18n.BW2bx_6z.0.0.50-internal.0.js → vue-i18n.D5WrXOZa.0.0.50-internal.2.js} +1 -1
  58. package/dist/interface/js/{vue-inline-svg.BJkQGmKz.0.0.50-internal.0.js → vue-inline-svg.R0uz_abA.0.0.50-internal.2.js} +1 -1
  59. package/dist/interface/js/{vue-router.B2mOXHaC.0.0.50-internal.0.js → vue-router.65mnGy-o.0.0.50-internal.2.js} +1 -1
  60. package/dist/interface/js/{vue-zoomable.BKDyCJE7.0.0.50-internal.0.js → vue-zoomable.B1Qop6Uu.0.0.50-internal.2.js} +1 -1
  61. package/dist/interface/js/{vue3-dnd.DRK24CBp.0.0.50-internal.0.js → vue3-dnd.CwmyAcir.0.0.50-internal.2.js} +1 -1
  62. package/dist/interface/js/{zoom-out.DxvzgVEo.0.0.50-internal.0.js → zoom-out.CMPALvRh.0.0.50-internal.2.js} +1 -1
  63. package/dist/interface/sw.js +1 -1
  64. package/dist/server/package.json +1 -1
  65. package/dist/server/src/rpc/server.js +4 -1
  66. package/dist/server/src/rpc/server.js.map +1 -1
  67. package/package.json +1 -1
  68. package/.prettierignore +0 -12
  69. package/.prettierrc.json +0 -12
  70. package/artillery.yaml +0 -29
  71. package/bin/cameraui-server.ts +0 -11
  72. package/dist/interface/js/Cammesh.D-6dAROQ.0.0.50-internal.0.js +0 -1
  73. package/dist/interface/js/Recordings.DhxjIW7e.0.0.50-internal.0.js +0 -1
  74. package/dist/interface/js/SettingsAppearance.DsTjKXk1.0.0.50-internal.0.js +0 -1
  75. package/dist/interface/js/SettingsRecordings.DgW7M0jj.0.0.50-internal.0.js +0 -1
  76. package/eslint.config.js +0 -103
  77. package/localdeps.txt +0 -10
  78. package/mypy.ini +0 -30
  79. package/requirements.txt +0 -15
  80. package/ruff.toml +0 -90
  81. package/src/__init__.py +0 -0
  82. package/src/_camera_ui_tools/__init__.py +0 -0
  83. package/src/_camera_ui_tools/__pycache__/__init__.cpython-311.pyc +0 -0
  84. package/src/api/controllers/api.controller.ts +0 -19
  85. package/src/api/controllers/auth.controller.ts +0 -294
  86. package/src/api/controllers/backup.controller.ts +0 -72
  87. package/src/api/controllers/cameras.controller.ts +0 -1029
  88. package/src/api/controllers/config.controller.ts +0 -102
  89. package/src/api/controllers/files.controller.ts +0 -35
  90. package/src/api/controllers/frameWorkers.controller.ts +0 -109
  91. package/src/api/controllers/plugins.controller.ts +0 -1206
  92. package/src/api/controllers/system.controller.ts +0 -273
  93. package/src/api/controllers/users.controller.ts +0 -648
  94. package/src/api/database/checks.ts +0 -268
  95. package/src/api/database/constants.ts +0 -8
  96. package/src/api/database/index.ts +0 -262
  97. package/src/api/database/migration.ts +0 -62
  98. package/src/api/database/selfcheck.ts +0 -492
  99. package/src/api/database/types.ts +0 -104
  100. package/src/api/go2rtc/api/application.ts +0 -48
  101. package/src/api/go2rtc/api/config.ts +0 -61
  102. package/src/api/go2rtc/api/discover.ts +0 -197
  103. package/src/api/go2rtc/api/index.ts +0 -49
  104. package/src/api/go2rtc/api/snapshot.ts +0 -44
  105. package/src/api/go2rtc/api/streams.ts +0 -168
  106. package/src/api/go2rtc/index.ts +0 -43
  107. package/src/api/go2rtc/queue.ts +0 -50
  108. package/src/api/go2rtc/types.ts +0 -174
  109. package/src/api/index.ts +0 -286
  110. package/src/api/middlewares/authPermission.middleware.ts +0 -46
  111. package/src/api/middlewares/authValidation.middleware.ts +0 -160
  112. package/src/api/middlewares/pagination.middleware.ts +0 -67
  113. package/src/api/middlewares/socketAuth.middleware.ts +0 -136
  114. package/src/api/plugins/header.plugin.ts +0 -15
  115. package/src/api/plugins/logger.plugin.ts +0 -44
  116. package/src/api/plugins/proxy.plugin.ts +0 -255
  117. package/src/api/plugins/socket.plugin.ts +0 -28
  118. package/src/api/plugins/system.plugin.ts +0 -48
  119. package/src/api/plugins/useragent.plugin.ts +0 -19
  120. package/src/api/routes/api.routes.ts +0 -10
  121. package/src/api/routes/auth.routes.ts +0 -56
  122. package/src/api/routes/backup.routes.ts +0 -28
  123. package/src/api/routes/cameras.routes.ts +0 -281
  124. package/src/api/routes/config.routes.ts +0 -66
  125. package/src/api/routes/files.routes.ts +0 -15
  126. package/src/api/routes/frameWorkers.routes.ts +0 -39
  127. package/src/api/routes/index.ts +0 -37
  128. package/src/api/routes/plugins.routes.ts +0 -420
  129. package/src/api/routes/system.routes.ts +0 -101
  130. package/src/api/routes/users.routes.ts +0 -167
  131. package/src/api/schemas/backup.schema.ts +0 -112
  132. package/src/api/schemas/cameras.schema.ts +0 -479
  133. package/src/api/schemas/config.schema.ts +0 -38
  134. package/src/api/schemas/go2rtc.schema.ts +0 -189
  135. package/src/api/schemas/plugins.schema.ts +0 -126
  136. package/src/api/schemas/storage.schema.ts +0 -11
  137. package/src/api/schemas/system.schema.ts +0 -45
  138. package/src/api/schemas/users.schema.ts +0 -185
  139. package/src/api/services/auth.service.ts +0 -210
  140. package/src/api/services/backup.service.ts +0 -144
  141. package/src/api/services/cameras.service.ts +0 -671
  142. package/src/api/services/plugins.service.ts +0 -470
  143. package/src/api/services/system.service.ts +0 -85
  144. package/src/api/services/users.service.ts +0 -276
  145. package/src/api/types/index.ts +0 -675
  146. package/src/api/utils/cert.ts +0 -254
  147. package/src/api/utils/constants.ts +0 -17
  148. package/src/api/utils/fetch.ts +0 -21
  149. package/src/api/utils/moveFiles.ts +0 -45
  150. package/src/api/utils/parse.ts +0 -17
  151. package/src/api/websocket/index.ts +0 -110
  152. package/src/api/websocket/nsp/logs.ts +0 -240
  153. package/src/api/websocket/nsp/main.ts +0 -27
  154. package/src/api/websocket/nsp/metrics.ts +0 -301
  155. package/src/api/websocket/nsp/notifications.ts +0 -43
  156. package/src/api/websocket/nsp/plugins.ts +0 -37
  157. package/src/api/websocket/nsp/server.ts +0 -220
  158. package/src/api/websocket/nsp/status.ts +0 -77
  159. package/src/api/websocket/types.ts +0 -55
  160. package/src/api.ts +0 -163
  161. package/src/camera/classes.ts +0 -32
  162. package/src/camera/controller.ts +0 -579
  163. package/src/camera/device.ts +0 -422
  164. package/src/camera/frameWorker.ts +0 -403
  165. package/src/camera/index.ts +0 -679
  166. package/src/camera/interfaces.ts +0 -95
  167. package/src/camera/iou.ts +0 -153
  168. package/src/camera/polygon.ts +0 -226
  169. package/src/camera/python/__init__.py +0 -0
  170. package/src/camera/python/__pycache__/__init__.cpython-311.pyc +0 -0
  171. package/src/camera/python/__pycache__/classes.cpython-311.pyc +0 -0
  172. package/src/camera/python/__pycache__/delegate_controller.cpython-311.pyc +0 -0
  173. package/src/camera/python/__pycache__/interfaces.cpython-311.pyc +0 -0
  174. package/src/camera/python/__pycache__/iou.cpython-310.pyc +0 -0
  175. package/src/camera/python/__pycache__/iou.cpython-311.pyc +0 -0
  176. package/src/camera/python/__pycache__/iou2.cpython-310.pyc +0 -0
  177. package/src/camera/python/__pycache__/polygon.cpython-311.pyc +0 -0
  178. package/src/camera/python/__pycache__/typings.cpython-311.pyc +0 -0
  179. package/src/camera/python/__pycache__/video_frame.cpython-311.pyc +0 -0
  180. package/src/camera/python/classes.py +0 -41
  181. package/src/camera/python/interfaces.py +0 -130
  182. package/src/camera/python/iou.py +0 -158
  183. package/src/camera/python/polygon.py +0 -230
  184. package/src/camera/python/test/__pycache__/polygon.cpython-310.pyc +0 -0
  185. package/src/camera/python/test/iou_test.py +0 -799
  186. package/src/camera/python/test/polygon.py +0 -238
  187. package/src/camera/python/test/polygon_test.py +0 -256
  188. package/src/camera/python/test/polygon_test2.py +0 -484
  189. package/src/camera/python/typings.py +0 -50
  190. package/src/camera/python/video_frame.py +0 -143
  191. package/src/camera/session.ts +0 -192
  192. package/src/camera/streaming/fmp4-session.ts +0 -390
  193. package/src/camera/streaming/peer-connection.ts +0 -273
  194. package/src/camera/streaming/rtsp-session.ts +0 -331
  195. package/src/camera/streaming/webrtc-connection.ts +0 -260
  196. package/src/camera/streaming/werift-session.ts +0 -317
  197. package/src/camera/test/iou_test.ts +0 -761
  198. package/src/camera/test/polygon.test.ts +0 -611
  199. package/src/camera/test/polygon_test.ts +0 -210
  200. package/src/camera/types.ts +0 -48
  201. package/src/camera/utils.ts +0 -489
  202. package/src/camera/videoFrame.ts +0 -123
  203. package/src/cloud/index.ts +0 -61
  204. package/src/cloud/remote/cloudflare.ts +0 -18
  205. package/src/cloud/remote/upnp.ts +0 -18
  206. package/src/cloud/remote/utils.ts +0 -24
  207. package/src/cloud/types.ts +0 -16
  208. package/src/decoder/child.ts +0 -883
  209. package/src/decoder/imageUtils.ts +0 -275
  210. package/src/decoder/manager.ts +0 -251
  211. package/src/decoder/python/__init__.py +0 -0
  212. package/src/decoder/python/__pycache__/__init__.cpython-311.pyc +0 -0
  213. package/src/decoder/python/__pycache__/image_utils.cpython-311.pyc +0 -0
  214. package/src/decoder/python/__pycache__/typings.cpython-311.pyc +0 -0
  215. package/src/decoder/python/__pycache__/utils.cpython-311.pyc +0 -0
  216. package/src/decoder/python/child.py +0 -903
  217. package/src/decoder/python/image_utils.py +0 -196
  218. package/src/decoder/python/pool.py +0 -45
  219. package/src/decoder/python/test/group.py +0 -62
  220. package/src/decoder/python/test/motion.mp4 +0 -0
  221. package/src/decoder/python/test/motion2.mp4 +0 -0
  222. package/src/decoder/python/test/opencl.cl +0 -70
  223. package/src/decoder/python/test/opencl2.cl +0 -130
  224. package/src/decoder/python/test/test.py +0 -427
  225. package/src/decoder/python/test/test1.jpg +0 -0
  226. package/src/decoder/python/test/test1_resized.jpg +0 -0
  227. package/src/decoder/python/test/test2.jpg +0 -0
  228. package/src/decoder/python/test/test2.py +0 -451
  229. package/src/decoder/python/test/test2_resized.jpg +0 -0
  230. package/src/decoder/python/test/test3.jpg +0 -0
  231. package/src/decoder/python/test/utils.py +0 -50
  232. package/src/decoder/python/typings.py +0 -57
  233. package/src/decoder/python/utils.py +0 -70
  234. package/src/decoder/test/child.ts +0 -117
  235. package/src/decoder/test/ffmpeg.ts +0 -371
  236. package/src/decoder/test/test.ts +0 -182
  237. package/src/decoder/types.ts +0 -27
  238. package/src/decoder/utils.ts +0 -64
  239. package/src/decoder/wasm/asconfig.json +0 -28
  240. package/src/decoder/wasm/assembly/console.ts +0 -1
  241. package/src/decoder/wasm/assembly/index.ts +0 -890
  242. package/src/decoder/wasm/build/decoder.d.ts +0 -43
  243. package/src/decoder/wasm/build/decoder.js +0 -108
  244. package/src/decoder/wasm/build/decoder.wasm +0 -0
  245. package/src/decoder/wasm/tsconfig.json +0 -5
  246. package/src/decoder/worker.ts +0 -113
  247. package/src/go2rtc/index.ts +0 -324
  248. package/src/go2rtc/types.ts +0 -88
  249. package/src/main.ts +0 -267
  250. package/src/manager/coreManager.ts +0 -78
  251. package/src/manager/deviceManager.ts +0 -132
  252. package/src/manager/types.ts +0 -17
  253. package/src/plugins/index.ts +0 -443
  254. package/src/plugins/plugin.ts +0 -177
  255. package/src/plugins/runtime/base.ts +0 -282
  256. package/src/plugins/runtime/index.ts +0 -15
  257. package/src/plugins/runtime/node/child.ts +0 -193
  258. package/src/plugins/runtime/node/index.ts +0 -67
  259. package/src/plugins/runtime/node/plugin.ts +0 -122
  260. package/src/plugins/runtime/node/pluginApi.ts +0 -27
  261. package/src/plugins/runtime/node/proxy/cameraDevice.ts +0 -299
  262. package/src/plugins/runtime/node/proxy/coreManager.ts +0 -128
  263. package/src/plugins/runtime/node/proxy/deviceManager.ts +0 -187
  264. package/src/plugins/runtime/node/storage.ts +0 -312
  265. package/src/plugins/runtime/node/storageController.ts +0 -91
  266. package/src/plugins/runtime/python/__init__.py +0 -0
  267. package/src/plugins/runtime/python/__pycache__/__init__.cpython-311.pyc +0 -0
  268. package/src/plugins/runtime/python/__pycache__/camera_storage.cpython-311.pyc +0 -0
  269. package/src/plugins/runtime/python/__pycache__/config_service.cpython-311.pyc +0 -0
  270. package/src/plugins/runtime/python/__pycache__/constants.cpython-311.pyc +0 -0
  271. package/src/plugins/runtime/python/__pycache__/ipc.cpython-311.pyc +0 -0
  272. package/src/plugins/runtime/python/__pycache__/logger.cpython-311.pyc +0 -0
  273. package/src/plugins/runtime/python/__pycache__/message_queue.cpython-311.pyc +0 -0
  274. package/src/plugins/runtime/python/__pycache__/namespaces.cpython-311.pyc +0 -0
  275. package/src/plugins/runtime/python/__pycache__/plugin.cpython-311.pyc +0 -0
  276. package/src/plugins/runtime/python/__pycache__/plugin_api.cpython-311.pyc +0 -0
  277. package/src/plugins/runtime/python/__pycache__/plugin_logger.cpython-311.pyc +0 -0
  278. package/src/plugins/runtime/python/__pycache__/proxy_utils.cpython-311.pyc +0 -0
  279. package/src/plugins/runtime/python/__pycache__/schema.cpython-311.pyc +0 -0
  280. package/src/plugins/runtime/python/__pycache__/storage.cpython-311.pyc +0 -0
  281. package/src/plugins/runtime/python/__pycache__/storage_controller.cpython-311.pyc +0 -0
  282. package/src/plugins/runtime/python/__pycache__/typings.cpython-311.pyc +0 -0
  283. package/src/plugins/runtime/python/child.py +0 -248
  284. package/src/plugins/runtime/python/index.ts +0 -73
  285. package/src/plugins/runtime/python/namespaces.py +0 -101
  286. package/src/plugins/runtime/python/plugin.py +0 -155
  287. package/src/plugins/runtime/python/plugin_api.py +0 -29
  288. package/src/plugins/runtime/python/proxy/__pycache__/camera_device.cpython-310.pyc +0 -0
  289. package/src/plugins/runtime/python/proxy/__pycache__/camera_device.cpython-311.pyc +0 -0
  290. package/src/plugins/runtime/python/proxy/__pycache__/camera_device.cpython-38.pyc +0 -0
  291. package/src/plugins/runtime/python/proxy/__pycache__/camera_device.cpython-39.pyc +0 -0
  292. package/src/plugins/runtime/python/proxy/__pycache__/constants.cpython-311.pyc +0 -0
  293. package/src/plugins/runtime/python/proxy/__pycache__/constants.cpython-39.pyc +0 -0
  294. package/src/plugins/runtime/python/proxy/__pycache__/core_manager.cpython-311.pyc +0 -0
  295. package/src/plugins/runtime/python/proxy/__pycache__/device_manager.cpython-310.pyc +0 -0
  296. package/src/plugins/runtime/python/proxy/__pycache__/device_manager.cpython-311.pyc +0 -0
  297. package/src/plugins/runtime/python/proxy/__pycache__/device_manager.cpython-38.pyc +0 -0
  298. package/src/plugins/runtime/python/proxy/__pycache__/device_manager.cpython-39.pyc +0 -0
  299. package/src/plugins/runtime/python/proxy/__pycache__/generate_keys.cpython-310.pyc +0 -0
  300. package/src/plugins/runtime/python/proxy/__pycache__/generate_keys.cpython-311.pyc +0 -0
  301. package/src/plugins/runtime/python/proxy/__pycache__/generate_keys.cpython-38.pyc +0 -0
  302. package/src/plugins/runtime/python/proxy/__pycache__/generate_keys.cpython-39.pyc +0 -0
  303. package/src/plugins/runtime/python/proxy/__pycache__/message_queue.cpython-310.pyc +0 -0
  304. package/src/plugins/runtime/python/proxy/__pycache__/message_queue.cpython-311.pyc +0 -0
  305. package/src/plugins/runtime/python/proxy/__pycache__/message_queue.cpython-38.pyc +0 -0
  306. package/src/plugins/runtime/python/proxy/__pycache__/message_queue.cpython-39.pyc +0 -0
  307. package/src/plugins/runtime/python/proxy/__pycache__/plugins_manager.cpython-310.pyc +0 -0
  308. package/src/plugins/runtime/python/proxy/__pycache__/plugins_manager.cpython-311.pyc +0 -0
  309. package/src/plugins/runtime/python/proxy/__pycache__/plugins_manager.cpython-38.pyc +0 -0
  310. package/src/plugins/runtime/python/proxy/__pycache__/plugins_manager.cpython-39.pyc +0 -0
  311. package/src/plugins/runtime/python/proxy/__pycache__/system_manager.cpython-310.pyc +0 -0
  312. package/src/plugins/runtime/python/proxy/__pycache__/system_manager.cpython-311.pyc +0 -0
  313. package/src/plugins/runtime/python/proxy/__pycache__/system_manager.cpython-38.pyc +0 -0
  314. package/src/plugins/runtime/python/proxy/__pycache__/system_manager.cpython-39.pyc +0 -0
  315. package/src/plugins/runtime/python/proxy/camera_device.py +0 -1732
  316. package/src/plugins/runtime/python/proxy/core_manager.py +0 -126
  317. package/src/plugins/runtime/python/proxy/device_manager.py +0 -197
  318. package/src/plugins/runtime/python/schema.py +0 -99
  319. package/src/plugins/runtime/python/storage.py +0 -353
  320. package/src/plugins/runtime/python/storage_controller.py +0 -102
  321. package/src/plugins/runtime/python/typings.py +0 -195
  322. package/src/plugins/schema.ts +0 -472
  323. package/src/plugins/types.ts +0 -60
  324. package/src/plugins/worker.ts +0 -250
  325. package/src/rpc/index.ts +0 -149
  326. package/src/rpc/namespaces.ts +0 -79
  327. package/src/rpc/server.ts +0 -422
  328. package/src/rpc/types.ts +0 -119
  329. package/src/rpc/utils.ts +0 -16
  330. package/src/services/config/constants.ts +0 -47
  331. package/src/services/config/defaults.ts +0 -57
  332. package/src/services/config/index.ts +0 -744
  333. package/src/services/config/types.ts +0 -211
  334. package/src/services/logger/index.ts +0 -96
  335. package/src/types.ts +0 -47
  336. package/src/utils/path.ts +0 -16
  337. package/src/utils/pythonInstaller.ts +0 -597
  338. package/tsconfig.json +0 -37
  339. package/types/fastify.d.ts +0 -14
  340. package/updates.config.js +0 -3
  341. /package/dist/interface/assets/{@xterm.DYP7pi_n.0.0.50-internal.0.css → @xterm.DYP7pi_n.0.0.50-internal.2.css} +0 -0
  342. /package/dist/interface/assets/{Adminpanel.BKek2EMH.0.0.50-internal.0.css → Adminpanel.BKek2EMH.0.0.50-internal.2.css} +0 -0
  343. /package/dist/interface/assets/{Cameras.BDWZJqPN.0.0.50-internal.0.css → Cameras.BDWZJqPN.0.0.50-internal.2.css} +0 -0
  344. /package/dist/interface/assets/{Camview.BFUFMoNX.0.0.50-internal.0.css → Camview.BFUFMoNX.0.0.50-internal.2.css} +0 -0
  345. /package/dist/interface/assets/{CuiConsole.DCYyxdVg.0.0.50-internal.0.css → CuiConsole.DCYyxdVg.0.0.50-internal.2.css} +0 -0
  346. /package/dist/interface/assets/{CuiPluginCard.yF8V0SZY.0.0.50-internal.0.css → CuiPluginCard.yF8V0SZY.0.0.50-internal.2.css} +0 -0
  347. /package/dist/interface/assets/{CuiTimeline.BkIwlYtw.0.0.50-internal.0.css → CuiTimeline.BkIwlYtw.0.0.50-internal.2.css} +0 -0
  348. /package/dist/interface/assets/{Plugin.DJElsAn5.0.0.50-internal.0.css → Plugin.DJElsAn5.0.0.50-internal.2.css} +0 -0
  349. /package/dist/interface/assets/{Plugins.0h77qnmN.0.0.50-internal.0.css → Plugins.0h77qnmN.0.0.50-internal.2.css} +0 -0
  350. /package/dist/interface/assets/{Settings.CkgfRkQr.0.0.50-internal.0.css → Settings.CkgfRkQr.0.0.50-internal.2.css} +0 -0
  351. /package/dist/interface/assets/{SettingsAccount.DeFul3eQ.0.0.50-internal.0.css → SettingsAccount.DeFul3eQ.0.0.50-internal.2.css} +0 -0
  352. /package/dist/interface/assets/{SettingsBackup.CX0rT0QJ.0.0.50-internal.0.css → SettingsBackup.CX0rT0QJ.0.0.50-internal.2.css} +0 -0
  353. /package/dist/interface/assets/{highlight.Ch2EwETz.0.0.50-internal.0.css → highlight.Ch2EwETz.0.0.50-internal.2.css} +0 -0
  354. /package/dist/interface/assets/{logo-256.BCQk9H1-.0.0.50-internal.0.png → logo-256.BCQk9H1-.0.0.50-internal.2.png} +0 -0
  355. /package/dist/interface/assets/{logo-512.DLKVOU2S.0.0.50-internal.0.png → logo-512.DLKVOU2S.0.0.50-internal.2.png} +0 -0
  356. /package/dist/interface/assets/{logo.DmEiRk0z.0.0.50-internal.0.png → logo.DmEiRk0z.0.0.50-internal.2.png} +0 -0
  357. /package/dist/interface/assets/{main.Cip9LkS3.0.0.50-internal.0.css → main.Cip9LkS3.0.0.50-internal.2.css} +0 -0
  358. /package/dist/interface/assets/{mode-json.Br7xisR1.0.0.50-internal.0.js → mode-json.Br7xisR1.0.0.50-internal.2.js} +0 -0
  359. /package/dist/interface/assets/{mode-yaml.CIYL8U3d.0.0.50-internal.0.js → mode-yaml.CIYL8U3d.0.0.50-internal.2.js} +0 -0
  360. /package/dist/interface/assets/{worker-base.jEXLGV2h.0.0.50-internal.0.js → worker-base.jEXLGV2h.0.0.50-internal.2.js} +0 -0
  361. /package/dist/interface/assets/{worker-json.DgwNIOck.0.0.50-internal.0.js → worker-json.DgwNIOck.0.0.50-internal.2.js} +0 -0
  362. /package/dist/interface/assets/{worker-yaml.B3uUYINB.0.0.50-internal.0.js → worker-yaml.B3uUYINB.0.0.50-internal.2.js} +0 -0
  363. /package/dist/interface/js/{@babel.l0sNRNKZ.0.0.50-internal.0.js → @babel.l0sNRNKZ.0.0.50-internal.2.js} +0 -0
  364. /package/dist/interface/js/{@intlify.BkGPX90O.0.0.50-internal.0.js → @intlify.BkGPX90O.0.0.50-internal.2.js} +0 -0
  365. /package/dist/interface/js/{@kurkle.D8fDXNIl.0.0.50-internal.0.js → @kurkle.D8fDXNIl.0.0.50-internal.2.js} +0 -0
  366. /package/dist/interface/js/{@primeuix.BFAY2Bk9.0.0.50-internal.0.js → @primeuix.BFAY2Bk9.0.0.50-internal.2.js} +0 -0
  367. /package/dist/interface/js/{@react-dnd.CcwRs9xb.0.0.50-internal.0.js → @react-dnd.CcwRs9xb.0.0.50-internal.2.js} +0 -0
  368. /package/dist/interface/js/{@socket.io.Dkula2eQ.0.0.50-internal.0.js → @socket.io.Dkula2eQ.0.0.50-internal.2.js} +0 -0
  369. /package/dist/interface/js/{@vue.B5vqhCp2.0.0.50-internal.0.js → @vue.B5vqhCp2.0.0.50-internal.2.js} +0 -0
  370. /package/dist/interface/js/{@xterm.CAa7rpjv.0.0.50-internal.0.js → @xterm.C-5Lxnx_.0.0.50-internal.2.js} +0 -0
  371. /package/dist/interface/js/{axios.DHr9lByg.0.0.50-internal.0.js → axios.DHr9lByg.0.0.50-internal.2.js} +0 -0
  372. /package/dist/interface/js/{birpc.l0sNRNKZ.0.0.50-internal.0.js → birpc.l0sNRNKZ.0.0.50-internal.2.js} +0 -0
  373. /package/dist/interface/js/{compare-versions.DqEOhma6.0.0.50-internal.0.js → compare-versions.DqEOhma6.0.0.50-internal.2.js} +0 -0
  374. /package/dist/interface/js/{dijkstrajs.C00ieaqj.0.0.50-internal.0.js → dijkstrajs.C00ieaqj.0.0.50-internal.2.js} +0 -0
  375. /package/dist/interface/js/{engine.io-parser.BiEtp6m2.0.0.50-internal.0.js → engine.io-parser.BiEtp6m2.0.0.50-internal.2.js} +0 -0
  376. /package/dist/interface/js/{entities.C20TfXL6.0.0.50-internal.0.js → entities.C20TfXL6.0.0.50-internal.2.js} +0 -0
  377. /package/dist/interface/js/{ev-emitter.X5f1qa-c.0.0.50-internal.0.js → ev-emitter.X5f1qa-c.0.0.50-internal.2.js} +0 -0
  378. /package/dist/interface/js/{get-size.D1dS-IaP.0.0.50-internal.0.js → get-size.D1dS-IaP.0.0.50-internal.2.js} +0 -0
  379. /package/dist/interface/js/{hookable.l0sNRNKZ.0.0.50-internal.0.js → hookable.l0sNRNKZ.0.0.50-internal.2.js} +0 -0
  380. /package/dist/interface/js/{mdurl.k9Sl0PQj.0.0.50-internal.0.js → mdurl.k9Sl0PQj.0.0.50-internal.2.js} +0 -0
  381. /package/dist/interface/js/{perfect-debounce.l0sNRNKZ.0.0.50-internal.0.js → perfect-debounce.l0sNRNKZ.0.0.50-internal.2.js} +0 -0
  382. /package/dist/interface/js/{primelocale.Bga3dOQu.0.0.50-internal.0.js → primelocale.Bga3dOQu.0.0.50-internal.2.js} +0 -0
  383. /package/dist/interface/js/{punycode.js.Dj65hjkv.0.0.50-internal.0.js → punycode.js.Dj65hjkv.0.0.50-internal.2.js} +0 -0
  384. /package/dist/interface/js/{react-dnd-html5-backend.1HQjlkwI.0.0.50-internal.0.js → react-dnd-html5-backend.1HQjlkwI.0.0.50-internal.2.js} +0 -0
  385. /package/dist/interface/js/{redux.BFxtlG_c.0.0.50-internal.0.js → redux.BFxtlG_c.0.0.50-internal.2.js} +0 -0
  386. /package/dist/interface/js/{tslib.9k7AwCpt.0.0.50-internal.0.js → tslib.9k7AwCpt.0.0.50-internal.2.js} +0 -0
  387. /package/dist/interface/js/{uc.micro.kMc2yuuw.0.0.50-internal.0.js → uc.micro.kMc2yuuw.0.0.50-internal.2.js} +0 -0
  388. /package/dist/interface/js/{uuid.C6aID195.0.0.50-internal.0.js → uuid.C6aID195.0.0.50-internal.2.js} +0 -0
  389. /package/dist/interface/js/{vue.l0sNRNKZ.0.0.50-internal.0.js → vue.l0sNRNKZ.0.0.50-internal.2.js} +0 -0
  390. /package/dist/interface/js/{zod.SKKegNRO.0.0.50-internal.0.js → zod.SKKegNRO.0.0.50-internal.2.js} +0 -0
@@ -1,903 +0,0 @@
1
- import asyncio
2
- import os
3
- import re
4
- import sys
5
- from collections.abc import AsyncGenerator
6
- from dataclasses import dataclass
7
- from datetime import datetime
8
- from typing import Any, Literal, Optional, Union, cast
9
-
10
- import setproctitle
11
-
12
- from _camera_ui_tools.camera_ui_common import LoggerService, SignalHandler
13
- from _camera_ui_tools.camera_ui_rpc_client import ClientInstance
14
- from _camera_ui_tools.camera_ui_rpc_client.utils.rpc import rpc
15
- from _camera_ui_tools.camera_ui_types import (
16
- CameraFrameWorkerDecoder,
17
- CameraFrameWorkerResolution,
18
- DecoderFormat,
19
- FfmpegArgs,
20
- FrameBuffer,
21
- FrameData,
22
- FrameMetadata,
23
- ImageInformation,
24
- ImageOptions,
25
- ImageOutputFormat,
26
- )
27
- from decoder.python.image_utils import process_image_threaded
28
- from decoder.python.typings import (
29
- BaseFrameWorkerConfig,
30
- FrameWorkerInterface,
31
- PixelFormat,
32
- WorkerToMainMessage,
33
- )
34
- from decoder.python.utils import TCPConnectionDetails, create_single_connection_server
35
- from plugins.runtime.python.namespaces import FrameWorkerCoreManagerNamespaces, NamespaceManager
36
- from plugins.runtime.python.typings import CoreManagerInterface
37
-
38
- process_name = (
39
- sys.argv[2] if len(sys.argv) > 2 else sys.argv[1] if len(sys.argv) > 1 else "camera.ui - Frame Worker"
40
- )
41
- setproctitle.setproctitle(process_name)
42
-
43
- PIXEL_FORMAT: PixelFormat = "yuv420p"
44
- METADATA_REGEX = r"s:\s*(\d+x\d+)"
45
-
46
-
47
- @dataclass
48
- class Frame:
49
- data: bytes
50
- timestamp: int
51
- metadata: FrameMetadata
52
-
53
-
54
- class FrameConsumer:
55
- def __init__(self, consumer_id: int, stream: "FFmpegStream") -> None:
56
- self.id: int = consumer_id
57
- self.stream: FFmpegStream = stream
58
- self.last_frame_timestamp: int = 0
59
- self.resolve_new_frame_future: Optional[asyncio.Future[None]] = None
60
-
61
- def notify_new_frame(self) -> None:
62
- if self.resolve_new_frame_future and not self.resolve_new_frame_future.done():
63
- self.resolve_new_frame_future.set_result(None)
64
- self.resolve_new_frame_future = None
65
-
66
- async def wait_for_new_frame(self) -> None:
67
- if self.resolve_new_frame_future is None or self.resolve_new_frame_future.done():
68
- self.resolve_new_frame_future = asyncio.Future()
69
- await self.resolve_new_frame_future
70
-
71
- def __aiter__(self) -> "FrameConsumer":
72
- return self
73
-
74
- async def __anext__(self) -> Frame:
75
- while True:
76
- frame = self.stream.get_next_frame(self.last_frame_timestamp)
77
-
78
- if frame:
79
- self.last_frame_timestamp = frame.timestamp
80
- return frame
81
-
82
- if not self.stream.stream_active:
83
- raise StopAsyncIteration
84
-
85
- # Wait for notification of new frame
86
- await self.wait_for_new_frame()
87
-
88
-
89
- class FFmpegStream:
90
- def __init__(self, worker: "FrameWorkerChild"):
91
- self.worker = worker
92
-
93
- width, height = map(int, worker.config["resolution"].split("x"))
94
- self.width = width
95
- self.height = height
96
-
97
- # Process Management
98
- self.ffmpeg_process: Optional[asyncio.subprocess.Process] = None
99
- self.active_viewers = 0
100
- self.ffmpeg_hw_options: Optional[FfmpegArgs] = None
101
- self.server: Optional[TCPConnectionDetails] = None
102
- self.socket: Optional[tuple[asyncio.StreamReader, asyncio.StreamWriter]] = None
103
- self.exit_task: Optional[asyncio.Task[Any]] = None
104
- self.frame_processing_task: Optional[asyncio.Task[Any]] = None
105
- self.stderr_task: Optional[asyncio.Task[Any]] = None
106
-
107
- # State
108
- self.stream_active = False
109
- self.metadata: Optional[FrameMetadata] = None
110
- self.is_shutting_down = False
111
- self.stream_start_timeout_sec = 60.0 # Timeout in Sekunden
112
- self.initialization_task: Optional[asyncio.Task[Any]] = None
113
-
114
- # Reconnection Logic
115
- self.reconnect_count = 0
116
-
117
- # Frame Queue Management
118
- self.frame_buffer: list[Frame] = []
119
- self.last_frame: Optional[Frame] = None
120
- self.queue_consumers: set[FrameConsumer] = set()
121
- self.frame_processing_active = False
122
- self.buffer_duration_ms = 5000
123
-
124
- # Health Check
125
- self.health_check_task: Optional[asyncio.Task[Any]] = None
126
- self.last_frame_timestamp = 0
127
- self.health_check_time = 5.0
128
- self.max_frame_time = 15.0
129
-
130
- # Consumer management
131
- self.next_consumer_id = 0
132
-
133
- @property
134
- def bytes_per_pixel(self) -> float:
135
- if self.pixel_format == "rgb24":
136
- return 3.0
137
- elif self.pixel_format in ["yuv420p", "nv12"]:
138
- return 1.5
139
- return 1.5
140
-
141
- @property
142
- def pixel_format(self) -> PixelFormat:
143
- return PIXEL_FORMAT
144
-
145
- @property
146
- def frame_size(self) -> int:
147
- return int(self.width * self.height * self.bytes_per_pixel)
148
-
149
- async def initialize(self) -> None:
150
- if self.initialization_task:
151
- await self.initialization_task
152
- return
153
-
154
- if self.stream_active:
155
- return
156
-
157
- try:
158
- self.initialization_task = asyncio.create_task(self._start_stream_process())
159
- await self.initialization_task
160
- finally:
161
- self.initialization_task = None
162
-
163
- def close(self) -> None:
164
- if not self.stream_active:
165
- return
166
-
167
- self.worker.logger.trace("Closing FFmpeg stream")
168
- self.cleanup_resources()
169
-
170
- def cleanup(self) -> None:
171
- self.is_shutting_down = True
172
- self.cleanup_resources()
173
-
174
- def register_viewer(self) -> FrameConsumer:
175
- self.active_viewers += 1
176
- consumer = FrameConsumer(self.next_consumer_id, self)
177
- self.next_consumer_id += 1
178
- self.queue_consumers.add(consumer)
179
- return consumer
180
-
181
- def unregister_viewer(self, consumer: FrameConsumer) -> None:
182
- self.queue_consumers.discard(consumer)
183
- self.active_viewers -= 1
184
- if self.active_viewers <= 0:
185
- self.active_viewers = 0
186
- self.close()
187
-
188
- def get_next_frame(self, timestamp: int) -> Optional[Frame]:
189
- if not self.last_frame or self.last_frame.timestamp <= timestamp:
190
- return None
191
-
192
- return Frame(
193
- data=bytes(self.last_frame.data),
194
- timestamp=self.last_frame.timestamp,
195
- metadata=self.last_frame.metadata,
196
- )
197
-
198
- def get_frame_by_timestamp(self, timestamp: int, max_difference: int = 0) -> Optional[Frame]:
199
- if not self.frame_buffer:
200
- return None
201
-
202
- # Quick boundary checks
203
- first_frame = self.frame_buffer[0]
204
- last_frame = self.frame_buffer[-1]
205
-
206
- # Check if timestamp is outside our buffer range (plus max_difference)
207
- if (
208
- timestamp < first_frame.timestamp - max_difference
209
- or timestamp > last_frame.timestamp + max_difference
210
- ):
211
- return None
212
-
213
- # Binary search for closest frame
214
- left = 0
215
- right = len(self.frame_buffer) - 1
216
-
217
- while left <= right:
218
- mid = (left + right) // 2
219
- frame = self.frame_buffer[mid]
220
- diff = frame.timestamp - timestamp
221
-
222
- # Exact match
223
- if diff == 0:
224
- return frame
225
-
226
- if diff < 0:
227
- left = mid + 1
228
- else:
229
- right = mid - 1
230
-
231
- # If we need exact match only
232
- if max_difference == 0:
233
- return None
234
-
235
- # Find closest frame within max_difference
236
- # After binary search, 'left' points to the insertion point
237
- # Check frames around this point
238
- candidates = [
239
- self.frame_buffer[max(0, left - 1)],
240
- self.frame_buffer[min(len(self.frame_buffer) - 1, left)],
241
- ]
242
-
243
- best_match = None
244
- smallest_diff = float("inf")
245
-
246
- for frame in candidates:
247
- diff = abs(frame.timestamp - timestamp)
248
- if diff <= max_difference and diff < smallest_diff:
249
- smallest_diff = diff
250
- best_match = frame
251
-
252
- if best_match:
253
- return Frame(
254
- data=bytes(best_match.data), timestamp=best_match.timestamp, metadata=best_match.metadata
255
- )
256
-
257
- return None
258
-
259
- async def _start_stream_process(self) -> None:
260
- try:
261
- await self._launch_stream()
262
- self.reconnect_count = 0
263
- except Exception as error:
264
- if self.is_shutting_down or self.active_viewers == 0:
265
- return
266
-
267
- self.reconnect_count = min(self.reconnect_count + 1, 10)
268
- delay_sec = self._calculate_reconnect_delay_sec()
269
-
270
- self.worker.logger.warn(f"Connection failed, retrying in {delay_sec}s")
271
- self.worker.logger.debug(f"Error: {str(error)}")
272
-
273
- await asyncio.sleep(delay_sec)
274
- self.worker.logger.debug("Retrying connection...")
275
- return await self._start_stream_process()
276
-
277
- async def _launch_stream(self) -> None:
278
- if self.active_viewers == 0:
279
- self.worker.logger.debug("No active consumers, not starting stream")
280
- return
281
-
282
- self.cleanup_resources()
283
-
284
- self.server = await create_single_connection_server("127.0.0.1", {"limit": self.frame_size * 10})
285
-
286
- ffmpeg_arguments = await self._prepare_ffmpeg_arguments(self.server.server_url)
287
- self.worker.logger.trace(
288
- f"Starting FFmpeg with args: {self.worker.config['ffmpegPath']} {' '.join(ffmpeg_arguments)}"
289
- )
290
-
291
- self.ffmpeg_process = await asyncio.create_subprocess_exec(
292
- self.worker.config["ffmpegPath"],
293
- *ffmpeg_arguments,
294
- stdin=asyncio.subprocess.DEVNULL,
295
- stdout=asyncio.subprocess.DEVNULL,
296
- stderr=asyncio.subprocess.PIPE,
297
- )
298
-
299
- self.exit_task = asyncio.create_task(self._on_ffmpeg_exit())
300
-
301
- try:
302
- reader, writer = await self.server.connection_promise
303
- self.socket = (reader, writer)
304
- self.metadata = await self._extract_metadata()
305
- self.stream_active = True
306
- self.frame_processing_task = asyncio.create_task(self._process_frames())
307
- self._start_health_check()
308
-
309
- except Exception as error:
310
- self.cleanup_resources()
311
- raise error
312
-
313
- async def _on_ffmpeg_exit(self) -> None:
314
- if not self.ffmpeg_process:
315
- return
316
-
317
- await self.ffmpeg_process.wait()
318
- if self.active_viewers > 0 and not self.is_shutting_down:
319
- self.worker.logger.warn("FFmpeg process exited unexpectedly, restarting in 10 seconds...")
320
- self.cleanup_resources()
321
- await asyncio.sleep(10)
322
- await self.initialize()
323
- else:
324
- self.worker.logger.debug("FFmpeg process exited")
325
-
326
- def _start_health_check(self) -> None:
327
- if self.health_check_task:
328
- self.health_check_task.cancel()
329
-
330
- async def health_check_loop() -> None:
331
- while self.stream_active and not self.is_shutting_down:
332
- await asyncio.sleep(self.health_check_time)
333
-
334
- if self.active_viewers == 0:
335
- continue
336
-
337
- now = int(datetime.now().timestamp() * 1000)
338
- time_since_last_frame_sec = (now - self.last_frame_timestamp) / 1000
339
-
340
- # If no frames for 30 seconds and we have active viewers, restart
341
- if (
342
- self.last_frame_timestamp > 0
343
- and time_since_last_frame_sec > self.max_frame_time
344
- and self.active_viewers > 0
345
- ):
346
- self.worker.logger.warn(
347
- f"No frames received in {time_since_last_frame_sec:.1f}s! This may be due to a network issue, FFmpeg failure or high load on the system."
348
- )
349
- self.worker.logger.log("Restarting...")
350
- self.cleanup_resources()
351
- asyncio.create_task(self.initialize())
352
- break
353
-
354
- self.health_check_task = asyncio.create_task(health_check_loop())
355
-
356
- async def _process_frames(self) -> None:
357
- if self.frame_processing_active or not self.socket or not self.metadata:
358
- return
359
-
360
- first_chunk_received = False
361
- first_frame_received = False
362
- self.frame_processing_active = True
363
- reader, _ = self.socket
364
- metadata = self.metadata
365
- buffer = bytearray()
366
- last_buffer_size = 0
367
-
368
- try:
369
- while True:
370
- chunk = await reader.read(8192)
371
-
372
- if not self.frame_processing_active:
373
- break
374
-
375
- if not chunk:
376
- continue
377
-
378
- if len(buffer) > self.frame_size * 10:
379
- self.worker.logger.warn(
380
- f"Buffer growing: {len(buffer)} Bytes "
381
- f"({len(buffer) / metadata['frameSize']:.2f} frames)"
382
- )
383
-
384
- if not first_chunk_received:
385
- self.worker.logger.trace("First chunk received")
386
- first_chunk_received = True
387
-
388
- buffer.extend(chunk)
389
-
390
- while len(buffer) >= self.frame_size:
391
- frame_data = bytes(buffer[: self.frame_size])
392
- buffer = buffer[self.frame_size :]
393
-
394
- if last_buffer_size == len(buffer) and len(buffer) > 0:
395
- self.worker.logger.warn(
396
- f"Possible issue: Buffer size not changing: {len(buffer)} bytes"
397
- )
398
- # buffer = bytearray()
399
- break
400
-
401
- last_buffer_size = len(buffer)
402
-
403
- if not first_frame_received:
404
- self.worker.logger.trace("First frame received")
405
- first_frame_received = True
406
-
407
- self._add_frame_to_queue(frame_data, self.metadata)
408
- except Exception as error:
409
- self.worker.logger.error(f"Error in frame processing: {str(error)}")
410
- finally:
411
- self.frame_processing_active = False
412
-
413
- def _add_frame_to_queue(self, frame_data: bytes, metadata: FrameMetadata) -> None:
414
- timestamp = int(datetime.now().timestamp() * 1000)
415
- self.last_frame_timestamp = timestamp
416
-
417
- new_frame = Frame(data=frame_data, timestamp=timestamp, metadata=metadata)
418
-
419
- self.frame_buffer.append(new_frame)
420
- self.last_frame = new_frame
421
-
422
- self._cleanup_old_frames()
423
-
424
- for consumer in self.queue_consumers:
425
- consumer.notify_new_frame()
426
-
427
- def _cleanup_old_frames(self) -> None:
428
- current_time = int(datetime.now().timestamp() * 1000)
429
- buffer_threshold = current_time - self.buffer_duration_ms
430
-
431
- while len(self.frame_buffer) > 1 and self.frame_buffer[0].timestamp < buffer_threshold:
432
- self.frame_buffer.pop(0)
433
-
434
- async def _extract_metadata(self) -> FrameMetadata:
435
- if not self.ffmpeg_process or not self.ffmpeg_process.stderr:
436
- raise RuntimeError("FFmpeg process not started or stderr not available")
437
-
438
- metadata_future: asyncio.Future[FrameMetadata] = asyncio.Future()
439
-
440
- async def read_stderr() -> None:
441
- try:
442
- assert self.ffmpeg_process and self.ffmpeg_process.stderr
443
-
444
- async for line in self.ffmpeg_process.stderr:
445
- if not metadata_future.done() and line:
446
- metadata = self._parse_metadata_from_stderr(line)
447
- if metadata:
448
- self.worker.logger.debug(f"Metadata received: {metadata}")
449
- metadata_future.set_result(metadata)
450
- except Exception as e:
451
- if not metadata_future.done():
452
- metadata_future.set_exception(e)
453
-
454
- self.stderr_task = asyncio.create_task(read_stderr())
455
-
456
- try:
457
- return await asyncio.wait_for(metadata_future, self.stream_start_timeout_sec)
458
- except asyncio.TimeoutError as e:
459
- raise TimeoutError("FFmpeg stream start timeout") from e
460
-
461
- def _calculate_reconnect_delay_sec(self) -> float:
462
- if self.reconnect_count == 10:
463
- return 60.0
464
- else:
465
- return 10.0
466
-
467
- def _parse_metadata_from_stderr(self, data: bytes) -> Optional[FrameMetadata]:
468
- stderr_text = data.decode("utf-8", errors="replace")
469
-
470
- match = re.search(METADATA_REGEX, stderr_text)
471
- if not match:
472
- return None
473
-
474
- resolution = match.group(1)
475
- original_width, original_height = map(int, resolution.split("x"))
476
- frame_data_size = self.frame_size
477
-
478
- metadata: FrameMetadata = {
479
- "format": "nv12" if self.worker.config["decoder"] == "gpu" else "yuv420p",
480
- "frameSize": frame_data_size,
481
- "width": self.width,
482
- "height": self.height,
483
- "origHeight": original_height,
484
- "origWidth": original_width,
485
- }
486
-
487
- return metadata
488
-
489
- async def _prepare_ffmpeg_arguments(self, server_url: str) -> list[str]:
490
- if not self.ffmpeg_hw_options:
491
- core_manager = await self._create_core_manager_proxy()
492
- self.ffmpeg_hw_options = (
493
- await core_manager.getHwaccelInfo(
494
- {
495
- "targetCodec": "h264",
496
- "keepOnHardware": True,
497
- "pixelFormat": self.pixel_format,
498
- "scale": {
499
- "width": self.width,
500
- "height": self.height,
501
- },
502
- }
503
- )
504
- )[0]
505
-
506
- filter_chain = ["showinfo=checksum=0"] + self.ffmpeg_hw_options["hwaccelFilters"]
507
- if self.worker.config["fps"] > 0:
508
- filter_chain.append(f"fps=fps={self.worker.config['fps']}")
509
-
510
- return [
511
- "-hide_banner",
512
- *self.ffmpeg_hw_options["hwaccelArgs"],
513
- "-fflags",
514
- "+discardcorrupt+flush_packets+nobuffer",
515
- "-flags",
516
- "low_delay",
517
- "-analyzeduration",
518
- "0",
519
- "-probesize",
520
- "500000",
521
- "-reorder_queue_size",
522
- "0",
523
- "-user_agent",
524
- "camera.ui/frameworker",
525
- "-timeout",
526
- "5000000",
527
- "-rtsp_transport",
528
- "tcp",
529
- "-re",
530
- "-i",
531
- self.worker.config["cameraSource"],
532
- *self.ffmpeg_hw_options["hwDeviceArgs"],
533
- "-fps_mode",
534
- "cfr",
535
- "-vf",
536
- ",".join(filter_chain),
537
- "-an",
538
- "-dn",
539
- "-sn",
540
- "-f",
541
- "rawvideo",
542
- f"{server_url}?tcp_nodelay=1",
543
- ]
544
-
545
- def cleanup_resources(self) -> None:
546
- self.stream_active = False
547
- self.metadata = None
548
- self.frame_processing_active = False
549
-
550
- if self.health_check_task:
551
- self.health_check_task.cancel()
552
- self.health_check_task = None
553
-
554
- if self.exit_task:
555
- self.exit_task.cancel()
556
- self.exit_task = None
557
-
558
- if self.frame_processing_task:
559
- self.frame_processing_task.cancel()
560
- self.frame_processing_task = None
561
-
562
- if self.stderr_task:
563
- self.stderr_task.cancel()
564
- self.stderr_task = None
565
-
566
- if self.server:
567
- self.server.terminate()
568
- self.server = None
569
-
570
- if self.socket:
571
- _, writer = self.socket
572
- writer.close()
573
- self.socket = None
574
-
575
- if self.ffmpeg_process:
576
- try:
577
- self.ffmpeg_process.kill()
578
- self.ffmpeg_process = None
579
- except Exception:
580
- pass
581
-
582
- async def _create_core_manager_proxy(self) -> CoreManagerInterface:
583
- return await self.worker.create_rpc_proxy(
584
- CoreManagerInterface, self.worker.namespaces["coreManagerRpc"]
585
- )
586
-
587
-
588
- class FrameWorkerChild(ClientInstance, FrameWorkerInterface):
589
- def __init__(self) -> None:
590
- super().__init__(
591
- "service",
592
- {
593
- "endpoints": os.environ["PROXY_ENDPOINTS"].split(","),
594
- "auth": {"user": os.environ["PROXY_USER"], "password": os.environ["PROXY_PASSWORD"]},
595
- "tls": {
596
- "cert": os.environ["PROXY_CERT"],
597
- "key": os.environ["PROXY_KEY"],
598
- "ca": os.environ["PROXY_CA"],
599
- },
600
- "metadata": {
601
- "id": NamespaceManager.frame_worker_namespaces(os.environ["CAMERA_ID"])[
602
- "frameWorkerChild"
603
- ],
604
- "name": "Frame Worker",
605
- },
606
- "bundled_connection": False,
607
- },
608
- )
609
-
610
- self.namespaces: FrameWorkerCoreManagerNamespaces = {
611
- **NamespaceManager.frame_worker_namespaces(os.environ["CAMERA_ID"]),
612
- **NamespaceManager.core_manager_namespaces(),
613
- }
614
-
615
- self.config: BaseFrameWorkerConfig = {
616
- "cameraId": os.environ["CAMERA_ID"],
617
- "cameraName": os.environ["CAMERA_NAME"],
618
- "cameraSource": os.environ["CAMERA_SOURCE"],
619
- "ffmpegPath": os.environ["FFMPEG_PATH"],
620
- "auth": {"user": os.environ["PROXY_USER"], "password": os.environ["PROXY_PASSWORD"]},
621
- "proxyEndpoints": os.environ["PROXY_ENDPOINTS"].split(","),
622
- "fps": int(os.environ["FPS"]),
623
- "resolution": cast(CameraFrameWorkerResolution, os.environ["RESOLUTION"]),
624
- "decoder": cast(CameraFrameWorkerDecoder, os.environ["DECODER"]),
625
- }
626
-
627
- for key in ["PROXY_USER", "PROXY_PASSWORD", "PROXY_ENDPOINTS", "PROXY_CERT", "PROXY_KEY", "PROXY_CA"]:
628
- if key in os.environ:
629
- del os.environ[key]
630
-
631
- self.logger = LoggerService(
632
- {
633
- "prefix": "Frame Worker",
634
- "suffix": self.config["cameraName"],
635
- "disable_timestamps": os.environ.get("LOGGER_DISABLE_TIMESTAMPS") == "1",
636
- "debug_enabled": os.environ.get("LOGGER_LEVEL") in ["debug", "trace"],
637
- "trace_enabled": os.environ.get("LOGGER_LEVEL") == "trace",
638
- }
639
- )
640
-
641
- self.ffmpeg_stream = FFmpegStream(self)
642
-
643
- self.signal_handler = SignalHandler(
644
- {
645
- "display_name": "[Signal]",
646
- "timeout_duration": 1000,
647
- "logger": self.logger,
648
- "close_function": self._close,
649
- }
650
- )
651
-
652
- self.stopped = False
653
-
654
- async def run(self) -> None:
655
- try:
656
- self.signal_handler.setup_handlers()
657
- await self.start()
658
- await self.signal_handler.shutdown_event.wait()
659
- except asyncio.CancelledError:
660
- pass
661
- finally:
662
- await self._close()
663
-
664
- async def on_start(self) -> None:
665
- await self.register_rpc_handler(self.namespaces["frameWorkerChildRpc"], self)
666
- await self._send_message({"message": "started", "data": {}})
667
-
668
- async def on_before_stop(self) -> None:
669
- return await self._close()
670
-
671
- async def on_stop(self) -> None:
672
- pass
673
-
674
- @rpc(public=True)
675
- async def generateFrames(self, options: Optional[ImageOptions] = None) -> AsyncGenerator[FrameData, None]:
676
- consumer = None
677
-
678
- try:
679
- consumer = self.ffmpeg_stream.register_viewer()
680
- await self.ffmpeg_stream.initialize()
681
-
682
- if not self.ffmpeg_stream.metadata:
683
- raise RuntimeError("Stream-Metadaten nicht verfügbar")
684
-
685
- metadata = self.ffmpeg_stream.metadata
686
-
687
- async for frame in consumer:
688
- validated_options = self._validate_options(metadata, options)
689
-
690
- if validated_options:
691
- processed_frame = await self.processFrame(
692
- frame.data,
693
- metadata["width"],
694
- metadata["height"],
695
- metadata["format"],
696
- validated_options,
697
- )
698
-
699
- yield {
700
- "data": processed_frame["image"],
701
- "metadata": metadata,
702
- "timestamp": frame.timestamp,
703
- "info": processed_frame["info"],
704
- }
705
- else:
706
- info = self._get_image_information(
707
- metadata["width"], metadata["height"], metadata["format"]
708
- )
709
-
710
- yield {
711
- "data": frame.data,
712
- "metadata": metadata,
713
- "timestamp": frame.timestamp,
714
- "info": info,
715
- }
716
- except Exception as error:
717
- self.logger.error(f"Error in frame generation: {error}")
718
- finally:
719
- if consumer:
720
- self.ffmpeg_stream.unregister_viewer(consumer)
721
-
722
- @rpc(public=True)
723
- async def getPrebufferedFrame(
724
- self,
725
- timestamp: int,
726
- options: Optional[ImageOptions] = None,
727
- max_difference: int = 500,
728
- ) -> Optional[FrameData]:
729
- prebuffered_frame = self.ffmpeg_stream.get_frame_by_timestamp(timestamp, max_difference)
730
- if not prebuffered_frame:
731
- self.logger.debug(f"No prebuffered frame found for timestamp {timestamp}")
732
- return None
733
-
734
- validated_options = options and self._validate_options(prebuffered_frame.metadata, options)
735
-
736
- if validated_options:
737
- processed_frame = await self.processFrame(
738
- prebuffered_frame.data,
739
- prebuffered_frame.metadata["width"],
740
- prebuffered_frame.metadata["height"],
741
- prebuffered_frame.metadata["format"],
742
- validated_options,
743
- )
744
-
745
- return {
746
- "data": processed_frame["image"],
747
- "metadata": prebuffered_frame.metadata,
748
- "timestamp": prebuffered_frame.timestamp,
749
- "info": processed_frame["info"],
750
- }
751
- else:
752
- info = self._get_image_information(
753
- prebuffered_frame.metadata["width"],
754
- prebuffered_frame.metadata["height"],
755
- prebuffered_frame.metadata["format"],
756
- )
757
-
758
- return {
759
- "data": prebuffered_frame.data,
760
- "metadata": prebuffered_frame.metadata,
761
- "timestamp": prebuffered_frame.timestamp,
762
- "info": info,
763
- }
764
-
765
- @rpc(public=True)
766
- async def processFrame(
767
- self,
768
- frame: bytes,
769
- width: int,
770
- height: int,
771
- format: Union[DecoderFormat, Literal["rgba"], Literal["gray"]],
772
- options: ImageOptions,
773
- ) -> FrameBuffer:
774
- new_frame = frame if isinstance(frame, bytes) else bytes(frame)
775
- info = self._get_image_information(width, height, format, options)
776
-
777
- image = await process_image_threaded(new_frame, width, height, format, options)
778
-
779
- return {"image": image, "info": info}
780
-
781
- def _validate_options(
782
- self, metadata: FrameMetadata, options: Optional[ImageOptions] = None
783
- ) -> Optional[ImageOptions]:
784
- if not options:
785
- return None
786
-
787
- validated: ImageOptions = {}
788
-
789
- # Format conversion
790
- metadata_format = "rgb" if metadata["format"] == "rgb24" else metadata["format"]
791
-
792
- if "format" in options and "to" in options["format"] and options["format"]["to"] != metadata_format:
793
- validated["format"] = options["format"]
794
-
795
- # Cropping
796
- if "crop" in options:
797
- crop = options["crop"]
798
- left = crop.get("left", 0)
799
- top = crop.get("top", 0)
800
-
801
- # Validate crop parameters
802
- if left >= metadata["width"] or top >= metadata["height"]:
803
- return None
804
-
805
- width = min(crop.get("width", metadata["width"]), metadata["width"] - left)
806
- height = min(crop.get("height", metadata["height"]), metadata["height"] - top)
807
-
808
- if width <= 0 or height <= 0:
809
- return None
810
-
811
- # Skip full frame "crops"
812
- if not (left == 0 and top == 0 and width == metadata["width"] and height == metadata["height"]):
813
- validated["crop"] = {"left": left, "top": top, "width": width, "height": height}
814
-
815
- # Resizing
816
- if "resize" in options:
817
- resize = options["resize"]
818
- target_width = resize.get("width", 0)
819
- target_height = resize.get("height", 0)
820
-
821
- # Handle negative values (which mean "use original")
822
- if target_width < 0:
823
- target_width = metadata["width"]
824
- if target_height < 0:
825
- target_height = metadata["height"]
826
-
827
- # Calculate missing dimension if only one is provided
828
- if target_width > 0 and target_height == 0:
829
- target_height = round((target_width * metadata["height"]) / metadata["width"])
830
- elif target_height > 0 and target_width == 0:
831
- target_width = round((target_height * metadata["width"]) / metadata["height"])
832
-
833
- # Skip if no resize needed
834
- if not (
835
- (target_width == 0 and target_height == 0)
836
- or (target_width == metadata["width"] and target_height == metadata["height"])
837
- ):
838
- validated["resize"] = {"width": target_width, "height": target_height}
839
-
840
- # Return None if no transformations are needed
841
- return validated if validated else None
842
-
843
- def _get_image_information(
844
- self,
845
- input_width: int,
846
- input_height: int,
847
- input_format: Union[DecoderFormat, Literal["rgba"], Literal["gray"]],
848
- options: Optional[ImageOptions] = None,
849
- ) -> ImageInformation:
850
- # Calculate output width
851
- width = input_width
852
- if options and "resize" in options and options["resize"].get("width"):
853
- width = options["resize"]["width"]
854
- elif options and "crop" in options:
855
- crop = options["crop"]
856
- left = crop.get("left", 0)
857
- crop_width = crop.get("width", input_width)
858
- width = min(crop_width, input_width - left)
859
-
860
- # Calculate output height
861
- height = input_height
862
- if options and "resize" in options and options["resize"].get("height"):
863
- height = options["resize"]["height"]
864
- elif options and "crop" in options:
865
- crop = options["crop"]
866
- top = crop.get("top", 0)
867
- crop_height = crop.get("height", input_height)
868
- height = min(crop_height, input_height - top)
869
-
870
- # Determine output format
871
- format: Union[DecoderFormat, ImageOutputFormat] = input_format
872
- if options and "format" in options and "to" in options["format"]:
873
- format = options["format"]["to"]
874
-
875
- format = "rgb" if format == "rgb24" else format
876
-
877
- # Calculate channels based on format
878
- channels = 1.5
879
-
880
- if format == "rgba":
881
- channels = 4
882
- elif format == "rgb":
883
- channels = 3
884
- elif format == "gray":
885
- channels = 1
886
-
887
- return {"width": width, "height": height, "channels": channels, "format": format}
888
-
889
- async def _send_message(self, message: WorkerToMainMessage) -> None:
890
- await self.send_to_server(message)
891
-
892
- async def _close(self) -> None:
893
- if self.stopped:
894
- return
895
-
896
- self.stopped = True
897
- self.ffmpeg_stream.cleanup()
898
- await self.stop()
899
-
900
-
901
- if __name__ == "__main__":
902
- worker = FrameWorkerChild()
903
- asyncio.run(worker.run())