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.
- nedo_vision_worker/__init__.py +1 -1
- nedo_vision_worker/services/AIModelClient.py +9 -16
- nedo_vision_worker/services/WorkerSourceUpdater.py +34 -10
- {nedo_vision_worker-1.2.4.dist-info → nedo_vision_worker-1.2.6.dist-info}/METADATA +1 -1
- {nedo_vision_worker-1.2.4.dist-info → nedo_vision_worker-1.2.6.dist-info}/RECORD +8 -8
- {nedo_vision_worker-1.2.4.dist-info → nedo_vision_worker-1.2.6.dist-info}/WHEEL +0 -0
- {nedo_vision_worker-1.2.4.dist-info → nedo_vision_worker-1.2.6.dist-info}/entry_points.txt +0 -0
- {nedo_vision_worker-1.2.4.dist-info → nedo_vision_worker-1.2.6.dist-info}/top_level.txt +0 -0
nedo_vision_worker/__init__.py
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
164
|
-
|
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
#
|
|
61
|
-
if source.status_code
|
|
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=
|
|
83
|
+
resolution=source.resolution,
|
|
72
84
|
status_code="disconnected",
|
|
73
|
-
frame_rate=
|
|
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(
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
95
|
-
nedo_vision_worker-1.2.
|
|
96
|
-
nedo_vision_worker-1.2.
|
|
97
|
-
nedo_vision_worker-1.2.
|
|
98
|
-
nedo_vision_worker-1.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|