nedo-vision-worker-core 0.3.4__py3-none-any.whl → 0.3.6__py3-none-any.whl

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.

Potentially problematic release.


This version of nedo-vision-worker-core might be problematic. Click here for more details.

Files changed (21) hide show
  1. nedo_vision_worker_core/__init__.py +1 -1
  2. nedo_vision_worker_core/database/DatabaseManager.py +17 -1
  3. nedo_vision_worker_core/pipeline/PipelineManager.py +63 -19
  4. nedo_vision_worker_core/pipeline/PipelineProcessor.py +23 -17
  5. nedo_vision_worker_core/pipeline/PipelineSyncThread.py +29 -32
  6. nedo_vision_worker_core/repositories/AIModelRepository.py +17 -17
  7. nedo_vision_worker_core/repositories/BaseRepository.py +44 -0
  8. nedo_vision_worker_core/repositories/PPEDetectionRepository.py +77 -79
  9. nedo_vision_worker_core/repositories/RestrictedAreaRepository.py +37 -38
  10. nedo_vision_worker_core/repositories/WorkerSourcePipelineDebugRepository.py +47 -46
  11. nedo_vision_worker_core/repositories/WorkerSourcePipelineDetectionRepository.py +14 -15
  12. nedo_vision_worker_core/repositories/WorkerSourcePipelineRepository.py +68 -36
  13. nedo_vision_worker_core/repositories/WorkerSourceRepository.py +9 -7
  14. nedo_vision_worker_core/streams/RTMPStreamer.py +283 -106
  15. nedo_vision_worker_core/streams/StreamSyncThread.py +51 -24
  16. nedo_vision_worker_core/streams/VideoStreamManager.py +76 -20
  17. {nedo_vision_worker_core-0.3.4.dist-info → nedo_vision_worker_core-0.3.6.dist-info}/METADATA +3 -2
  18. {nedo_vision_worker_core-0.3.4.dist-info → nedo_vision_worker_core-0.3.6.dist-info}/RECORD +21 -20
  19. {nedo_vision_worker_core-0.3.4.dist-info → nedo_vision_worker_core-0.3.6.dist-info}/WHEEL +0 -0
  20. {nedo_vision_worker_core-0.3.4.dist-info → nedo_vision_worker_core-0.3.6.dist-info}/entry_points.txt +0 -0
  21. {nedo_vision_worker_core-0.3.4.dist-info → nedo_vision_worker_core-0.3.6.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  import time
3
3
  import threading
4
- import cv2
5
4
  from typing import Any, Dict, Optional
6
5
 
7
6
  from .VideoStream import VideoStream
@@ -18,6 +17,11 @@ class VideoStreamManager:
18
17
  self.direct_device_streams: Dict[Any, Dict[str, Any]] = {}
19
18
  # Per-direct-device locks: {worker_source_id: threading.Lock}
20
19
  self.direct_device_locks: Dict[Any, threading.Lock] = {}
20
+
21
+ # Reference counting for lazy loading: {worker_source_id: set of pipeline_ids}
22
+ self.stream_references: Dict[Any, set] = {}
23
+ # Store URLs for streams that aren't started yet: {worker_source_id: url}
24
+ self.pending_streams: Dict[Any, str] = {}
21
25
 
22
26
  self.shared_device_manager = SharedVideoDeviceManager()
23
27
 
@@ -38,16 +42,71 @@ class VideoStreamManager:
38
42
  # -----------------------
39
43
  # Public API
40
44
  # -----------------------
41
- def add_stream(self, worker_source_id, url):
42
- """Adds and starts a stream (regular file/RTSP or a shared direct device) if not already present."""
45
+ def register_stream(self, worker_source_id, url):
46
+ """Register a stream URL without starting it (lazy loading)."""
43
47
  with self._lock:
48
+ if worker_source_id not in self.pending_streams:
49
+ self.pending_streams[worker_source_id] = url
50
+ logging.debug(f"📝 Registered stream {worker_source_id} for lazy loading")
51
+
52
+ def unregister_stream(self, worker_source_id):
53
+ """Unregister a stream that's no longer available in the database."""
54
+ with self._lock:
55
+ # If it's pending, remove it
56
+ if worker_source_id in self.pending_streams:
57
+ del self.pending_streams[worker_source_id]
58
+ logging.debug(f"🗑️ Unregistered pending stream {worker_source_id}")
59
+
60
+ # If it's active and has no references, remove it
61
+ if worker_source_id in self.stream_references:
62
+ if len(self.stream_references[worker_source_id]) == 0:
63
+ self._stop_stream(worker_source_id)
64
+
65
+ def acquire_stream(self, worker_source_id, pipeline_id):
66
+ """Request access to a stream for a pipeline. Starts the stream if not already running."""
67
+ with self._lock:
68
+ # Initialize reference set if needed
69
+ if worker_source_id not in self.stream_references:
70
+ self.stream_references[worker_source_id] = set()
71
+
72
+ # Add pipeline reference
73
+ self.stream_references[worker_source_id].add(pipeline_id)
74
+ logging.info(f"🔗 Pipeline {pipeline_id} acquired stream {worker_source_id} (refs: {len(self.stream_references[worker_source_id])})")
75
+
76
+ # If stream is already running, we're done
44
77
  if worker_source_id in self.streams or worker_source_id in self.direct_device_streams:
45
- logging.warning("⚠️ Stream %s is already active.", worker_source_id)
78
+ return True
79
+
80
+ # Get URL from pending streams
81
+ url = self.pending_streams.get(worker_source_id)
82
+ if not url:
83
+ logging.error(f"❌ Cannot acquire stream {worker_source_id}: URL not registered")
84
+ return False
85
+
86
+ # Start the stream (outside lock to avoid blocking)
87
+ return self._start_stream(worker_source_id, url)
88
+
89
+ def release_stream(self, worker_source_id, pipeline_id):
90
+ """Release a stream reference from a pipeline. Stops the stream if no more references."""
91
+ with self._lock:
92
+ if worker_source_id not in self.stream_references:
46
93
  return
47
-
94
+
95
+ # Remove pipeline reference
96
+ self.stream_references[worker_source_id].discard(pipeline_id)
97
+ ref_count = len(self.stream_references[worker_source_id])
98
+ logging.info(f"🔓 Pipeline {pipeline_id} released stream {worker_source_id} (refs: {ref_count})")
99
+
100
+ # If no more references, stop the stream
101
+ if ref_count == 0:
102
+ logging.info(f"💤 Stream {worker_source_id} has no more references, stopping...")
103
+ self._stop_stream(worker_source_id)
104
+
105
+ def _start_stream(self, worker_source_id, url):
106
+ """Internal method to actually start a stream."""
48
107
  if self._is_direct_device(url):
49
108
  self._add_direct_device_stream(worker_source_id, url)
50
- return
109
+ return True
51
110
 
52
111
  # Regular stream
53
112
  stream = VideoStream(url)
@@ -55,18 +114,19 @@ class VideoStreamManager:
55
114
  stream.start() # start thread
56
115
  with self._lock:
57
116
  self.streams[worker_source_id] = stream
58
- logging.info("✅ Added and started video stream: %s", worker_source_id)
117
+ logging.info("✅ Started video stream: %s", worker_source_id)
118
+ return True
59
119
  except Exception as e:
60
120
  logging.error("❌ Failed to start regular stream %s: %s", worker_source_id, e)
61
-
62
- def remove_stream(self, worker_source_id):
63
- """Stops and removes a stream (regular or direct device)."""
64
- if not worker_source_id:
65
- return
66
-
121
+ return False
122
+
123
+ def _stop_stream(self, worker_source_id):
124
+ """Internal method to stop a stream."""
67
125
  # Direct device?
68
126
  with self._lock:
69
127
  is_direct = worker_source_id in self.direct_device_streams
128
+ # Clean up references
129
+ self.stream_references.pop(worker_source_id, None)
70
130
 
71
131
  if is_direct:
72
132
  self._remove_direct_device_stream(worker_source_id)
@@ -77,20 +137,16 @@ class VideoStreamManager:
77
137
  stream = self.streams.pop(worker_source_id, None)
78
138
 
79
139
  if stream is None:
80
- logging.warning("⚠️ Stream %s not found in manager.", worker_source_id)
81
140
  return
82
141
 
83
- logging.info("🛑 Removing video stream: %s", worker_source_id)
142
+ logging.info("🛑 Stopping video stream: %s", worker_source_id)
84
143
  try:
85
- # Expectation: VideoStream.stop() should signal and join internally.
86
144
  stream.stop()
87
145
  except Exception as e:
88
146
  logging.error("❌ Error stopping stream %s: %s", worker_source_id, e)
89
147
  finally:
90
148
  stream = None
91
149
 
92
- logging.info("✅ Stream %s removed successfully.", worker_source_id)
93
-
94
150
  def start_all(self):
95
151
  """Starts all regular streams that are not alive. (Direct devices are publisher-driven.)"""
96
152
  logging.info("🔄 Starting all video streams...")
@@ -114,13 +170,13 @@ class VideoStreamManager:
114
170
 
115
171
  for wid in regular_ids:
116
172
  try:
117
- self.remove_stream(wid)
173
+ self._stop_stream(wid)
118
174
  except Exception as e:
119
175
  logging.error("Error stopping regular stream %s: %s", wid, e)
120
176
 
121
177
  for wid in direct_ids:
122
178
  try:
123
- self.remove_stream(wid)
179
+ self._stop_stream(wid)
124
180
  except Exception as e:
125
181
  logging.error("Error stopping direct device stream %s: %s", wid, e)
126
182
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedo-vision-worker-core
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Nedo Vision Worker Core Library for AI Vision Processing
5
5
  Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
6
6
  Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
@@ -31,7 +31,6 @@ Requires-Python: >=3.8
31
31
  Description-Content-Type: text/markdown
32
32
  Requires-Dist: alembic>=1.8.0
33
33
  Requires-Dist: numpy>=1.21.0
34
- Requires-Dist: opencv-python>=4.6.0
35
34
  Requires-Dist: pillow>=8.0.0
36
35
  Requires-Dist: psutil>=5.9.0
37
36
  Requires-Dist: scipy>=1.9.0
@@ -41,6 +40,8 @@ Requires-Dist: torch>=1.9.0
41
40
  Requires-Dist: torchvision>=0.10.0
42
41
  Requires-Dist: ultralytics>=8.0.0
43
42
  Requires-Dist: rfdetr<2.0.0,>=1.2.0
43
+ Provides-Extra: opencv
44
+ Requires-Dist: opencv-python>=4.6.0; extra == "opencv"
44
45
  Provides-Extra: dev
45
46
  Requires-Dist: pytest>=7.0.0; extra == "dev"
46
47
  Requires-Dist: black>=22.0.0; extra == "dev"
@@ -1,4 +1,4 @@
1
- nedo_vision_worker_core/__init__.py,sha256=0iPoN0eHddv9i7aZ8YJKl2Rn_P_BTsxTJMPggoVd_kc,1924
1
+ nedo_vision_worker_core/__init__.py,sha256=E4gmykhnzrU8ANXRIGP9LQG8TNbk6eAyuWqtAsFdj7Y,1924
2
2
  nedo_vision_worker_core/cli.py,sha256=8YuKWsIgICUYXE_QtwyU3WzGhVjTWiAo5uzpFOmjNc8,5766
3
3
  nedo_vision_worker_core/core_service.py,sha256=dnHNjbslOeyeWqHDFnk_yKdfTICYzLyRIcuZNwF0Zf4,11323
4
4
  nedo_vision_worker_core/doctor.py,sha256=K_-hVV2-mdEefZ4Cfu5hMCiOxBiI1aXY8VtkkpK80Lc,10651
@@ -11,7 +11,7 @@ nedo_vision_worker_core/callbacks/DetectionCallbackTypes.py,sha256=U7Qb0dCMtOHuZ
11
11
  nedo_vision_worker_core/callbacks/__init__.py,sha256=JBoYu2FKZd6f_zG4M0gsuXBQEMMvdJgOpzcsg7Lxf5U,499
12
12
  nedo_vision_worker_core/config/ConfigurationManager.py,sha256=s-uh2xT2bmot6egvAjVo_4FPurcYoq1W62FzGTExPoo,6870
13
13
  nedo_vision_worker_core/config/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
14
- nedo_vision_worker_core/database/DatabaseManager.py,sha256=o2e9jcqBbtf0Zd9TYuyQhXyKj9rjnNYbSShr7CxuAPk,9341
14
+ nedo_vision_worker_core/database/DatabaseManager.py,sha256=EDSz6auDx3i-DofHJBZdcEWyDHXqCwFB54WTBu9ExME,10314
15
15
  nedo_vision_worker_core/database/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
16
16
  nedo_vision_worker_core/detection/BaseDetector.py,sha256=bReQCTy4tEA1itvw3kkjBMQnx3Jn4MjnDkzdtPwmNPQ,757
17
17
  nedo_vision_worker_core/detection/RFDETRDetector.py,sha256=3T3zTFZW0pBv9E-pSpY4JP7wI0LOTM4hxzanvrEXMcE,3093
@@ -59,31 +59,32 @@ nedo_vision_worker_core/models/worker_source_pipeline_debug.py,sha256=6S7TkN37Fr
59
59
  nedo_vision_worker_core/models/worker_source_pipeline_detection.py,sha256=p6CJsiVCKprTYrNxJsiTB8njXdHkjZKVEyBceRVE6fY,560
60
60
  nedo_vision_worker_core/pipeline/ModelManager.py,sha256=K7lmVOo-KL7bnWtyafilZs23bzd6loCgfUz7xuAmlVw,6195
61
61
  nedo_vision_worker_core/pipeline/PipelineConfigManager.py,sha256=X55i9GyXcW9ylO6cj2UMAZFSxxPViacL4H4DZl60CAY,1157
62
- nedo_vision_worker_core/pipeline/PipelineManager.py,sha256=dAYLK5AXXUkfO7cyxLEGGa9SCQTP-9iAO_otffRgVsI,5482
62
+ nedo_vision_worker_core/pipeline/PipelineManager.py,sha256=S3QxTcJjDhOY2O8x9c62kYXotgjV4enlCJLcziZEIh8,7589
63
63
  nedo_vision_worker_core/pipeline/PipelinePrepocessor.py,sha256=cCiVSHHqsKCtKYURdYoEjHJX2GnT6zd8kQ6ZukjQ3V0,1271
64
- nedo_vision_worker_core/pipeline/PipelineProcessor.py,sha256=92ef6fRpibY3VXNeydpD1kprRCRH3fye_cpztAEVrLQ,26350
65
- nedo_vision_worker_core/pipeline/PipelineSyncThread.py,sha256=LX_LXU9MTdD5rP-ElHcYR431_qTXNaKOWcCsE4E9Gd0,8613
64
+ nedo_vision_worker_core/pipeline/PipelineProcessor.py,sha256=KMdFDvQO2yI9NxjNDwRQU07St1Y01QgM97tgNuyelw0,26766
65
+ nedo_vision_worker_core/pipeline/PipelineSyncThread.py,sha256=2tIqheE2BG-DAEqUgq9i4blz0vQWmnYu8MgHPLJkg3g,8704
66
66
  nedo_vision_worker_core/pipeline/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
67
67
  nedo_vision_worker_core/preprocessing/ImageResizer.py,sha256=RvOazxe6dJQuiy0ZH4lIGbdFfiu0FLUVCHoMvxkDNT4,1324
68
68
  nedo_vision_worker_core/preprocessing/ImageRoi.py,sha256=iO7oQ-SdUSA_kTIVBuq_mdycXsiJNfiFD3J7-VTxiQ4,2141
69
69
  nedo_vision_worker_core/preprocessing/Preprocessor.py,sha256=uYIh0Ld4T1zEEHtKVLbUVBcF0kUwj5zCfPXn__bKwwU,477
70
70
  nedo_vision_worker_core/preprocessing/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
71
- nedo_vision_worker_core/repositories/AIModelRepository.py,sha256=SzBLwUrjDnc4QayZOA2zL0Jl8YQ-x6S1rde_zCBu6LQ,1591
72
- nedo_vision_worker_core/repositories/PPEDetectionRepository.py,sha256=C_0QL2sHiSlM9rPmhLmfs6hdZk9FDazy-aVLcznN5w0,6623
73
- nedo_vision_worker_core/repositories/RestrictedAreaRepository.py,sha256=umZ7IrgoEFqAa9ZZlH7KPUIbmp5yhBc0FSDUOY6UFag,4283
74
- nedo_vision_worker_core/repositories/WorkerSourcePipelineDebugRepository.py,sha256=_z-UyaYtg1Q5nqSbs_16ngYesyM3aji4VrP1ZBHm6Jk,2987
75
- nedo_vision_worker_core/repositories/WorkerSourcePipelineDetectionRepository.py,sha256=FxPOKrDjgebr8BqJ8SvbyNMsvHwHNcbFpcQ33QW6Whw,2616
76
- nedo_vision_worker_core/repositories/WorkerSourcePipelineRepository.py,sha256=8V0eUVG4iOQJUcOkQVuICyVpzT86N9A25vBqNHe4Jq8,3284
77
- nedo_vision_worker_core/repositories/WorkerSourceRepository.py,sha256=vypj82LrxaPkte16xHI6YKonTzx3o2aGdh_CTZgywMA,617
71
+ nedo_vision_worker_core/repositories/AIModelRepository.py,sha256=97KcDB-oB72NdIU6gNXUGh2rzM5R-TjaBg8F7VY-3OA,1601
72
+ nedo_vision_worker_core/repositories/BaseRepository.py,sha256=FvdcD8I8_4_6TMZa8XrX26XZ6CAD8j0B0nvwZSZ9xLQ,1471
73
+ nedo_vision_worker_core/repositories/PPEDetectionRepository.py,sha256=dIOPUU7xOCJ8A5v6AnBVKwtKPVlc5M5cUhLCWumTXyo,6882
74
+ nedo_vision_worker_core/repositories/RestrictedAreaRepository.py,sha256=a5Vc8WLTtAa6Tn-ZSKkBEw4-cM29VW8WUwhbH7YfM9E,4416
75
+ nedo_vision_worker_core/repositories/WorkerSourcePipelineDebugRepository.py,sha256=lN_yip6woya9YUA5sYKbTyDQz2qSfgqkr3YP2hSd9ws,3211
76
+ nedo_vision_worker_core/repositories/WorkerSourcePipelineDetectionRepository.py,sha256=5m4lvmIETJSGDH1T1EHuUDWC-13t5I860UbN_uzEj9A,2641
77
+ nedo_vision_worker_core/repositories/WorkerSourcePipelineRepository.py,sha256=vwwRA1INuK66siOHNZxSBX8CE9uEW8VVcCIA7dmshKo,4714
78
+ nedo_vision_worker_core/repositories/WorkerSourceRepository.py,sha256=-a-UlsopPlJWlY36QUodPEjSZVE3BDoLgsVAioiNOo0,663
78
79
  nedo_vision_worker_core/repositories/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
79
80
  nedo_vision_worker_core/services/SharedVideoStreamServer.py,sha256=rhCineMKPG3GQbrMHlSHP4xhXaGZ6Rn1oqIajW5xpaY,9827
80
81
  nedo_vision_worker_core/services/VideoSharingDaemon.py,sha256=iY6afEKTOsphfHvmZTL0grezka2DS9DDq-1EIpVMy0Y,28524
81
82
  nedo_vision_worker_core/services/VideoSharingDaemonManager.py,sha256=sc8VZo5iwoOdR8uTiel5BKz6-eZ7wwLy3IwV_3tsAu0,10340
82
- nedo_vision_worker_core/streams/RTMPStreamer.py,sha256=Dblfutc1UVHj159KUHFYZ8xFEVhHVknZn_nAqKR6uCs,8695
83
+ nedo_vision_worker_core/streams/RTMPStreamer.py,sha256=0eThAwEyOg1ZuO0zbIvdqOICGQDRoOPxEcOBQtskM7A,17110
83
84
  nedo_vision_worker_core/streams/SharedVideoDeviceManager.py,sha256=vSslwxbhKH6FPndR1HcSFIVWtF-iiOQMlSa4VvFa6M4,16265
84
- nedo_vision_worker_core/streams/StreamSyncThread.py,sha256=WmYAY9wFiFhLlxGdnvKGIjAqLwCBayNKdmAWzkbU0jM,3763
85
+ nedo_vision_worker_core/streams/StreamSyncThread.py,sha256=ETT0N_P90ksn6Q5pb7NvMadqCuoicz_g52lcDkHIp88,5382
85
86
  nedo_vision_worker_core/streams/VideoStream.py,sha256=nGtJ4FAZ1Ek-8hVRopEt0bLWLpa10OtyUwdDEuXLObQ,13343
86
- nedo_vision_worker_core/streams/VideoStreamManager.py,sha256=i7uemC3tUdsrAhL8lvoxCYa0fwUh5oAVD6DDaxflXGE,12057
87
+ nedo_vision_worker_core/streams/VideoStreamManager.py,sha256=g5cz-YXPewSubBXxCg4mfzsuGKoOHXu-SrMxaGjYPHw,14956
87
88
  nedo_vision_worker_core/streams/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
88
89
  nedo_vision_worker_core/tracker/SFSORT.py,sha256=0kggw0l4yPZ55AKHdqVX6mu9ehHmJed7jcJ3JQoC4sk,14061
89
90
  nedo_vision_worker_core/tracker/TrackerManager.py,sha256=xtDMI657W2s7HM2lMGtwU0x5Hq74BZpLHd-5xk-278I,6152
@@ -96,8 +97,8 @@ nedo_vision_worker_core/util/PersonRestrictedAreaMatcher.py,sha256=iuzCU32BQKaZ3
96
97
  nedo_vision_worker_core/util/PlatformDetector.py,sha256=GGL8UfeMQITR22EMYIRWnuOEnSqo7Dr5mb0PaFrl8AM,3006
97
98
  nedo_vision_worker_core/util/TablePrinter.py,sha256=wzLGgb1GFMeIbAP6HmKcZD33j4D-IlyqlyeR7C5yD7w,1137
98
99
  nedo_vision_worker_core/util/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
99
- nedo_vision_worker_core-0.3.4.dist-info/METADATA,sha256=3_UyMqyQc4m_h1qyECq1k7w3vWnN2Hr-ixnLK_BPEp0,14370
100
- nedo_vision_worker_core-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
101
- nedo_vision_worker_core-0.3.4.dist-info/entry_points.txt,sha256=pIPafsvPnBw-fpBKBmc1NQCQ6PQY3ad8mZ6mn8_p5FI,70
102
- nedo_vision_worker_core-0.3.4.dist-info/top_level.txt,sha256=y8kusXjVYqtG8MSHYWTrk8bRrvjOrphKXYyzu943TTQ,24
103
- nedo_vision_worker_core-0.3.4.dist-info/RECORD,,
100
+ nedo_vision_worker_core-0.3.6.dist-info/METADATA,sha256=bGu8iMZGYWUU3zPPN23EwAZenJNJlayPcGzE-AT6sl0,14412
101
+ nedo_vision_worker_core-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
102
+ nedo_vision_worker_core-0.3.6.dist-info/entry_points.txt,sha256=pIPafsvPnBw-fpBKBmc1NQCQ6PQY3ad8mZ6mn8_p5FI,70
103
+ nedo_vision_worker_core-0.3.6.dist-info/top_level.txt,sha256=y8kusXjVYqtG8MSHYWTrk8bRrvjOrphKXYyzu943TTQ,24
104
+ nedo_vision_worker_core-0.3.6.dist-info/RECORD,,