nedo-vision-worker 1.2.4__py3-none-any.whl → 1.2.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.
@@ -6,5 +6,5 @@ A library for running worker agents in the Nedo Vision platform.
6
6
 
7
7
  from .worker_service import WorkerService
8
8
 
9
- __version__ = "1.2.4"
9
+ __version__ = "1.2.6"
10
10
  __all__ = ["WorkerService"]
@@ -98,7 +98,7 @@ class AIModelClient(GrpcClientBase):
98
98
  if download_info.thread and download_info.thread.is_alive():
99
99
  download_info.thread.join(timeout=5)
100
100
 
101
- self._update_model_status(model_id, DownloadState.CANCELLED.value, "Download cancelled")
101
+ self._update_model_status(model_id, "cancelled", "Download cancelled")
102
102
  logging.info(f"🛑 Cancelled download for {download_info.model_name}")
103
103
  return True
104
104
 
@@ -160,11 +160,8 @@ class AIModelClient(GrpcClientBase):
160
160
 
161
161
  def _handle_existing_model(self, server_model, local_model, updated_models: List):
162
162
  if not self._model_file_exists(local_model.file):
163
- if not self._is_downloading(server_model.id):
164
- logging.warning(f"⚠️ Model file missing for {local_model.name}. Re-downloading...")
165
- self._schedule_download(server_model)
166
- else:
167
- logging.info(f"⏳ Model {local_model.name} is already being downloaded, skipping reschedule")
163
+ logging.warning(f"⚠️ Model file missing for {local_model.name}. Re-downloading...")
164
+ self._schedule_download(server_model)
168
165
  return
169
166
 
170
167
  needs_update, changes = self._check_model_changes(server_model, local_model)
@@ -196,6 +193,7 @@ class AIModelClient(GrpcClientBase):
196
193
  self._schedule_download(server_model)
197
194
 
198
195
  def _check_model_changes(self, server_model, local_model) -> tuple[bool, List[str]]:
196
+ """Check if model needs update and return list of changes"""
199
197
  changes = []
200
198
 
201
199
  if server_model.version != local_model.version:
@@ -248,22 +246,16 @@ class AIModelClient(GrpcClientBase):
248
246
  local_model.set_main_class(server_model.main_class)
249
247
 
250
248
  def _schedule_download(self, model):
251
- if self._is_downloading(model.id):
252
- logging.info(f"⏳ Model {model.name} download already in progress, skipping reschedule")
253
- return
254
-
255
249
  self._cancel_download(model.id)
256
250
 
257
251
  download_info = DownloadInfo(
258
252
  model_id=model.id,
259
253
  model_name=model.name,
260
254
  version=model.version,
261
- state=DownloadState.PENDING,
262
255
  start_time=time.time()
263
256
  )
264
257
 
265
- self._set_download_info(model.id, download_info)
266
- self._update_model_status(model.id, DownloadState.PENDING.value)
258
+ self._update_model_status(model.id, "pending")
267
259
 
268
260
  download_info.thread = threading.Thread(
269
261
  target=self._download_worker,
@@ -272,11 +264,12 @@ class AIModelClient(GrpcClientBase):
272
264
  name=f"ModelDownload-{model.id[:8]}"
273
265
  )
274
266
  download_info.thread.start()
267
+ self._set_download_info(model.id, download_info)
275
268
 
276
269
  def _download_worker(self, model, download_info: DownloadInfo):
277
270
  try:
278
271
  download_info.state = DownloadState.DOWNLOADING
279
- self._update_model_status(model.id, DownloadState.DOWNLOADING.value)
272
+ self._update_model_status(model.id, "downloading")
280
273
  logging.info(f"📥 Downloading {model.name}...")
281
274
 
282
275
  success = self._download_model_file(model, download_info)
@@ -285,7 +278,7 @@ class AIModelClient(GrpcClientBase):
285
278
  download_info.state = DownloadState.COMPLETED
286
279
  download_info.end_time = time.time()
287
280
  duration = download_info.end_time - download_info.start_time
288
- self._update_model_status(model.id, DownloadState.COMPLETED.value)
281
+ self._update_model_status(model.id, "completed")
289
282
  logging.info(f"✅ Downloaded {model.name} in {duration:.1f}s")
290
283
  else:
291
284
  self._handle_download_failure(download_info, "Download failed")
@@ -298,7 +291,7 @@ class AIModelClient(GrpcClientBase):
298
291
  def _handle_download_failure(self, download_info: DownloadInfo, error: str):
299
292
  download_info.state = DownloadState.FAILED
300
293
  download_info.error_message = error
301
- self._update_model_status(download_info.model_id, DownloadState.FAILED.value, error)
294
+ self._update_model_status(download_info.model_id, "failed", error)
302
295
  logging.error(f"❌ Failed to download {download_info.model_name}: {error}")
303
296
 
304
297
  def _save_changes(self, new_models: List, updated_models: List, models_to_delete: List):
@@ -36,12 +36,25 @@ class WorkerSourceUpdater:
36
36
  url = source.file_path
37
37
 
38
38
  if not url:
39
+ logger.debug(f"[WorkerSource {source.id}] No URL available for metadata probe")
39
40
  return None
40
41
 
41
42
  if source.type_code == "file":
42
43
  url = self.source_file_path / os.path.basename(url)
43
44
 
44
- return VideoProbeUtil.get_video_metadata(url)
45
+ try:
46
+ metadata = VideoProbeUtil.get_video_metadata(url)
47
+ if metadata:
48
+ logger.debug(
49
+ f"[WorkerSource {source.id}] Metadata retrieved: "
50
+ f"resolution={metadata.get('resolution')}, frame_rate={metadata.get('frame_rate')}"
51
+ )
52
+ else:
53
+ logger.warning(f"[WorkerSource {source.id}] Metadata probe returned None for URL: {url}")
54
+ return metadata
55
+ except Exception as e:
56
+ logger.error(f"[WorkerSource {source.id}] Error getting metadata: {e}")
57
+ return None
45
58
 
46
59
  def update_worker_sources(self):
47
60
  """Fetch local worker sources, probe video URLs, and update if different from the local DB.
@@ -57,34 +70,40 @@ class WorkerSourceUpdater:
57
70
  metadata = self._get_source_metadata(source)
58
71
  if not metadata:
59
72
  logger.warning(f"⚠️ [APP] Failed to probe video for Worker Source ID {source.id} (type: {source.type_code}, url: {source.url if hasattr(source, 'url') else 'N/A'})")
60
- # Set disconnected status for failed probes
61
- if source.status_code != "disconnected":
73
+ # Only set disconnected if currently connected (avoid flip-flopping)
74
+ if source.status_code == "connected":
62
75
  source.status_code = "disconnected"
63
- source.resolution = None
64
- source.frame_rate = None
65
76
  updated_records.append(source)
66
77
 
67
78
  # Send gRPC update for disconnected status
79
+ # Keep existing resolution and frame_rate - don't overwrite with None
68
80
  worker_timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
69
81
  response = self.client.update_worker_source(
70
82
  worker_source_id=source.id,
71
- resolution=None,
83
+ resolution=source.resolution,
72
84
  status_code="disconnected",
73
- frame_rate=None,
85
+ frame_rate=source.frame_rate,
74
86
  worker_timestamp=worker_timestamp,
75
87
  token=self.token,
76
88
  )
77
89
 
78
90
  if response.get("success"):
79
- logger.info(f"✅ [APP] Updated Worker Source ID {source.id} to disconnected")
91
+ logger.info(f"✅ [APP] Updated Worker Source ID {source.id} to disconnected (kept existing resolution/framerate)")
80
92
  else:
81
93
  logger.error(f"🚨 [APP] Failed to update Worker Source ID {source.id} to disconnected: {response.get('message')}")
82
94
  continue
83
95
 
84
96
  # Extract details
85
97
  resolution = metadata.get("resolution")
86
- frame_rate = round(metadata.get("frame_rate"), 0) if metadata.get("frame_rate") else None
98
+ frame_rate = int(round(metadata.get("frame_rate"), 0)) if metadata.get("frame_rate") else None
87
99
  status_code = "connected" if resolution else "disconnected"
100
+
101
+ if resolution is None and source.resolution is not None:
102
+ resolution = source.resolution
103
+
104
+ if frame_rate is None and source.frame_rate is not None:
105
+ frame_rate = source.frame_rate
106
+
88
107
  # .NET Compatible time
89
108
  worker_timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
90
109
 
@@ -94,7 +113,12 @@ class WorkerSourceUpdater:
94
113
  source.frame_rate != frame_rate or
95
114
  source.status_code != status_code
96
115
  ):
97
- logger.info(f"🔄 [APP] Detected changes in Worker Source ID {source.id}, updating...")
116
+ logger.info(
117
+ f"🔄 [APP] Detected changes in Worker Source ID {source.id}, updating... "
118
+ f"[resolution: {source.resolution} -> {resolution}, "
119
+ f"frame_rate: {source.frame_rate} -> {frame_rate}, "
120
+ f"status: {source.status_code} -> {status_code}]"
121
+ )
98
122
 
99
123
  # Update local database
100
124
  source.resolution = resolution
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedo-vision-worker
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: Nedo Vision Worker Service 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>
@@ -1,4 +1,4 @@
1
- nedo_vision_worker/__init__.py,sha256=a-NxvsF-pe7DHXveR0Yl8pRtnvvBPSNTgQn4SbPpDi4,203
1
+ nedo_vision_worker/__init__.py,sha256=3uSTVntcW747CFtMZ_qyKrptoNgHDvLNjg0pdgb4otM,203
2
2
  nedo_vision_worker/cli.py,sha256=ddWspJmSgVkcUYvRdkvTtMNuMTDvNCqLLuMVU9KE3Ik,7457
3
3
  nedo_vision_worker/doctor.py,sha256=wNkpe8gLVd76Y_ViyK2h1ZFdqeSl37MnzZN5frWKu30,48410
4
4
  nedo_vision_worker/worker_service.py,sha256=rXUVmyxcJPGhQEZ4UQvjQS5UqlnLBYudHQZCj0dQDxo,10421
@@ -47,7 +47,7 @@ nedo_vision_worker/repositories/WorkerSourcePipelineDetectionRepository.py,sha25
47
47
  nedo_vision_worker/repositories/WorkerSourcePipelineRepository.py,sha256=xfmEvgnyt-DdfSApGyFfy0H0dXjFFkjeo4LMr0fVFXU,10053
48
48
  nedo_vision_worker/repositories/WorkerSourceRepository.py,sha256=AhAJLAacMFdsOgtQNiu7Pahl1DAGI0T1THHeUlKwQJc,2385
49
49
  nedo_vision_worker/repositories/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
50
- nedo_vision_worker/services/AIModelClient.py,sha256=P1vgavdnmqnxQaRzULqH9A3XC39iub8LlteTjcN8hPg,15840
50
+ nedo_vision_worker/services/AIModelClient.py,sha256=lxRNax6FR-pV0G1NpJnlaqjbQeu3kRolIUNSw1RkoZA,15406
51
51
  nedo_vision_worker/services/ConnectionInfoClient.py,sha256=toC9zuY2Hrx1Cwq8Gycy_iFlaG1DvFT4qewlLlitpEQ,2214
52
52
  nedo_vision_worker/services/DatasetSourceClient.py,sha256=O5a7onxFl0z47zXaMXWxHAMPuuc-i_vzkd2w5fwrukc,3319
53
53
  nedo_vision_worker/services/DirectDeviceToRTMPStreamer.py,sha256=M5ei0cd3_KDhHZp6EkrOowhAY-hAHfAQh9YDVjQtbQI,22278
@@ -66,7 +66,7 @@ nedo_vision_worker/services/VideoSharingDaemon.py,sha256=hYMjUIKNUVT1qSxuUuHN-7B
66
66
  nedo_vision_worker/services/VideoStreamClient.py,sha256=QSgUV3LijYrNdnBG1ylABOdUaSatQamfXaqJhAiol9M,7260
67
67
  nedo_vision_worker/services/WorkerSourceClient.py,sha256=vDZeCuHL5QQ2-knZ4TOSA59jzmbbThGIwFKKLEZ72Ws,9198
68
68
  nedo_vision_worker/services/WorkerSourcePipelineClient.py,sha256=qaBx9T2gWMzpqZaeQdbIeklsXNwzWD5kqgB41rrSkBI,17135
69
- nedo_vision_worker/services/WorkerSourceUpdater.py,sha256=r_pCL1NiUlgPUFrntE1DWFG-KJygZPK51lAUGPwlzxo,7758
69
+ nedo_vision_worker/services/WorkerSourceUpdater.py,sha256=4t_CEHBLGDRvvuQS6eEPMivTI11ZuzusKKto6t9tPIk,9115
70
70
  nedo_vision_worker/services/WorkerStatusClient.py,sha256=7kC5EZjEBwWtHOE6UQ29OPCpYnv_6HSuH7Tc0alK_2Q,2531
71
71
  nedo_vision_worker/services/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
72
72
  nedo_vision_worker/util/FFmpegUtil.py,sha256=QnQrzurmllzGb7SlAAYCrzKBUblweoFU-0h-X-32IYg,1829
@@ -91,8 +91,8 @@ nedo_vision_worker/worker/SystemUsageManager.py,sha256=StutV4UyLUfduYfK20de4SbPd
91
91
  nedo_vision_worker/worker/VideoStreamWorker.py,sha256=5n6v1PNO7IB-jj_McALLkUP-cBjJoIEw4UiSAs3vTb0,7606
92
92
  nedo_vision_worker/worker/WorkerManager.py,sha256=T0vMfhOd7YesgQ9o2w6soeJ6n9chUAcuwcGe7p31xr0,5298
93
93
  nedo_vision_worker/worker/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
94
- nedo_vision_worker-1.2.4.dist-info/METADATA,sha256=fc_VtfHAkfNgM0tOaipzU6x2LZgrtmlA8Q5JtHfRdd4,14661
95
- nedo_vision_worker-1.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
- nedo_vision_worker-1.2.4.dist-info/entry_points.txt,sha256=LrglS-8nCi8C_PL_pa6uxdgCe879hBETHDVXAckvs-8,60
97
- nedo_vision_worker-1.2.4.dist-info/top_level.txt,sha256=vgilhlkyD34YsEKkaBabmhIpcKSvF3XpzD2By68L-XI,19
98
- nedo_vision_worker-1.2.4.dist-info/RECORD,,
94
+ nedo_vision_worker-1.2.6.dist-info/METADATA,sha256=irbHs4_-uj182kff6PpW0YND59Q_UwhsPVLb5wI5NNQ,14661
95
+ nedo_vision_worker-1.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
+ nedo_vision_worker-1.2.6.dist-info/entry_points.txt,sha256=LrglS-8nCi8C_PL_pa6uxdgCe879hBETHDVXAckvs-8,60
97
+ nedo_vision_worker-1.2.6.dist-info/top_level.txt,sha256=vgilhlkyD34YsEKkaBabmhIpcKSvF3XpzD2By68L-XI,19
98
+ nedo_vision_worker-1.2.6.dist-info/RECORD,,