nedo-vision-worker-core 0.3.6__py3-none-any.whl → 0.3.7__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.

@@ -7,7 +7,7 @@ A library for running AI vision processing and detection in the Nedo Vision plat
7
7
  from .core_service import CoreService
8
8
  from .callbacks import DetectionType, CallbackTrigger, DetectionData, IntervalMetadata
9
9
 
10
- __version__ = "0.3.6"
10
+ __version__ = "0.3.7"
11
11
  __all__ = [
12
12
  "CoreService",
13
13
  "DetectionType",
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Column, String
1
+ from sqlalchemy import Column, String, DateTime
2
2
  from sqlalchemy.orm import relationship
3
3
  from ..database.DatabaseManager import Base
4
4
 
@@ -13,6 +13,7 @@ class WorkerSourcePipelineEntity(Base):
13
13
  ai_model_id = Column(String, nullable=True)
14
14
  pipeline_status_code = Column(String, nullable=False)
15
15
  location_name = Column(String, nullable=True)
16
+ last_preview_request_at = Column(DateTime, nullable=True)
16
17
 
17
18
  worker_source_pipeline_configs = relationship(
18
19
  "WorkerSourcePipelineConfigEntity",
@@ -8,12 +8,14 @@ from .PipelineConfigManager import PipelineConfigManager
8
8
  from .PipelinePrepocessor import PipelinePrepocessor
9
9
  from ..repositories.WorkerSourcePipelineDebugRepository import WorkerSourcePipelineDebugRepository
10
10
  from ..repositories.WorkerSourcePipelineDetectionRepository import WorkerSourcePipelineDetectionRepository
11
+ from ..repositories.WorkerSourcePipelineRepository import WorkerSourcePipelineRepository
11
12
  from ..streams.VideoStreamManager import VideoStreamManager
12
13
  from ..ai.VideoDebugger import VideoDebugger
13
14
  from ..ai.FrameDrawer import FrameDrawer
14
15
  from ..tracker.TrackerManager import TrackerManager
15
16
  from ..detection.BaseDetector import BaseDetector
16
17
  from ..streams.RTMPStreamer import RTMPStreamer
18
+ from ..util.PipelinePreviewChecker import PipelinePreviewChecker
17
19
 
18
20
 
19
21
  class PipelineProcessor:
@@ -41,6 +43,10 @@ class PipelineProcessor:
41
43
  self.worker_source_id = pipeline.worker_source_id
42
44
 
43
45
  self.rtmp_streamer = None
46
+ self.rtmp_streaming_active = False
47
+ self.last_preview_check_time = 0
48
+ self.preview_check_interval = 5.0 # Check every 5 seconds
49
+ self.pipeline_repo = WorkerSourcePipelineRepository()
44
50
 
45
51
  self.detection_processor_codes = [
46
52
  PPEDetectionProcessor.code,
@@ -202,26 +208,42 @@ class PipelineProcessor:
202
208
  logging.warning(f"Debug save failed: {e}")
203
209
  self.debug_flag = False
204
210
 
205
- # Push frame to RTMP stream
206
- # RTMPStreamer handles its own restarts internally
207
- if self.rtmp_streamer is None:
208
- try:
209
- self.rtmp_streamer = RTMPStreamer(self.pipeline_id)
210
- logging.info(f"🎬 RTMP streamer initialized for pipeline {pipeline_id}")
211
- except Exception as e:
212
- logging.error(f"❌ Failed to initialize RTMP streamer for pipeline {pipeline_id}: {e}")
213
- self.rtmp_streamer = None
211
+ # Check if RTMP streaming should be active based on preview requests
212
+ current_time = time.time()
213
+ if current_time - self.last_preview_check_time >= self.preview_check_interval:
214
+ self._check_and_update_rtmp_streaming()
215
+ self.last_preview_check_time = current_time
214
216
 
215
- if self.rtmp_streamer:
216
- try:
217
- self.rtmp_streamer.push_frame(drawn_frame)
218
- except Exception as e:
219
- logging.error(f"❌ RTMP push error for pipeline {pipeline_id}: {e}")
220
- if "initialization_failed" in str(e).lower():
221
- try:
222
- self.rtmp_streamer.stop_stream()
223
- except Exception:
224
- pass
217
+ # Push frame to RTMP stream if preview is active
218
+ if self.rtmp_streaming_active:
219
+ if self.rtmp_streamer is None:
220
+ try:
221
+ self.rtmp_streamer = RTMPStreamer(self.pipeline_id)
222
+ logging.info(f"🎬 RTMP streamer initialized for pipeline {pipeline_id} (preview requested)")
223
+ except Exception as e:
224
+ logging.error(f"❌ Failed to initialize RTMP streamer for pipeline {pipeline_id}: {e}")
225
+ self.rtmp_streamer = None
226
+
227
+ if self.rtmp_streamer:
228
+ try:
229
+ self.rtmp_streamer.push_frame(drawn_frame)
230
+ except Exception as e:
231
+ logging.error(f"❌ RTMP push error for pipeline {pipeline_id}: {e}")
232
+ if "initialization_failed" in str(e).lower():
233
+ try:
234
+ self.rtmp_streamer.stop_stream()
235
+ except Exception:
236
+ pass
237
+ self.rtmp_streamer = None
238
+ else:
239
+ # Stop RTMP streaming if preview is no longer active
240
+ if self.rtmp_streamer is not None:
241
+ try:
242
+ logging.info(f"🛑 Stopping RTMP streamer for pipeline {pipeline_id} (preview expired)")
243
+ self.rtmp_streamer.stop_stream()
244
+ except Exception as e:
245
+ logging.error(f"❌ Error stopping RTMP streamer: {e}")
246
+ finally:
225
247
  self.rtmp_streamer = None
226
248
 
227
249
  # feed detection worker with latest-only behavior
@@ -538,6 +560,33 @@ class PipelineProcessor:
538
560
  self.consecutive_frame_failures = 0
539
561
  self.last_successful_frame_time = time.time()
540
562
 
563
+ def _check_and_update_rtmp_streaming(self):
564
+ """
565
+ Check if RTMP streaming should be active based on preview requests.
566
+ Updates the rtmp_streaming_active flag.
567
+ """
568
+ try:
569
+ # Get fresh pipeline data from database
570
+ pipeline = self.pipeline_repo.get_worker_source_pipeline(self.pipeline_id)
571
+
572
+ if not pipeline:
573
+ logging.warning(f"⚠️ Pipeline {self.pipeline_id} not found in database")
574
+ self.rtmp_streaming_active = False
575
+ return
576
+
577
+ # Check if preview is active using the utility
578
+ should_stream = PipelinePreviewChecker.should_stream_rtmp(
579
+ pipeline.last_preview_request_at,
580
+ preview_window_seconds=300 # 5 minutes
581
+ )
582
+
583
+ self.rtmp_streaming_active = should_stream
584
+
585
+ except Exception as e:
586
+ logging.error(f"❌ Error checking preview status for pipeline {self.pipeline_id}: {e}")
587
+ # On error, disable streaming to be safe
588
+ self.rtmp_streaming_active = False
589
+
541
590
  def reset_frame_failure_counters(self):
542
591
  logging.info(f"🔄 Resetting frame failure counters for pipeline {self.pipeline_id}")
543
592
  self.consecutive_frame_failures = 0
@@ -0,0 +1,50 @@
1
+ from datetime import datetime
2
+
3
+ class PipelinePreviewChecker:
4
+ """
5
+ Utility class to check if RTMP streaming should be enabled for a pipeline
6
+ based on the last preview request timestamp.
7
+ """
8
+
9
+ @staticmethod
10
+ def should_stream_rtmp(last_preview_request_at, preview_window_seconds=300):
11
+ """
12
+ Check if RTMP streaming should be enabled based on the last preview request.
13
+
14
+ Args:
15
+ last_preview_request_at: DateTime object or None representing the last preview request
16
+ preview_window_seconds: Time window in seconds to keep streaming after a request (default: 300 = 5 minutes)
17
+
18
+ Returns:
19
+ bool: True if streaming should be enabled, False otherwise
20
+ """
21
+ if last_preview_request_at is None:
22
+ return False
23
+
24
+ # Calculate the time difference
25
+ current_time = datetime.utcnow()
26
+ time_since_request = current_time - last_preview_request_at
27
+
28
+ # Check if we're within the preview window
29
+ return time_since_request.total_seconds() <= preview_window_seconds
30
+
31
+ @staticmethod
32
+ def get_remaining_preview_time(last_preview_request_at, preview_window_seconds=300):
33
+ """
34
+ Get the remaining time (in seconds) for the preview window.
35
+
36
+ Args:
37
+ last_preview_request_at: DateTime object or None representing the last preview request
38
+ preview_window_seconds: Time window in seconds (default: 300 = 5 minutes)
39
+
40
+ Returns:
41
+ int: Remaining seconds in the preview window, or 0 if expired/not requested
42
+ """
43
+ if last_preview_request_at is None:
44
+ return 0
45
+
46
+ current_time = datetime.utcnow()
47
+ time_since_request = current_time - last_preview_request_at
48
+ remaining = preview_window_seconds - time_since_request.total_seconds()
49
+
50
+ return max(0, int(remaining))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedo-vision-worker-core
3
- Version: 0.3.6
3
+ Version: 0.3.7
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>
@@ -1,4 +1,4 @@
1
- nedo_vision_worker_core/__init__.py,sha256=E4gmykhnzrU8ANXRIGP9LQG8TNbk6eAyuWqtAsFdj7Y,1924
1
+ nedo_vision_worker_core/__init__.py,sha256=PqquZ5-JOZg5UUcsfuxszP0e6O1jt9jItruqFR6Rekk,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
@@ -53,7 +53,7 @@ nedo_vision_worker_core/models/ppe_detection_label.py,sha256=qON7a0fuDv5cK8phGH0
53
53
  nedo_vision_worker_core/models/restricted_area_violation.py,sha256=0enCi3tv15YMy3NaI6FwqhmLYHbbVX4nWTh46qKxrWc,829
54
54
  nedo_vision_worker_core/models/user.py,sha256=SnLUz2nS7j17bIP-gElMEaR-jWdnNQ0fTpRminVKY60,294
55
55
  nedo_vision_worker_core/models/worker_source.py,sha256=FB8irZ26LhCKNHBcpIIb5Mi3SoSNm9-q25VIkO5jQWg,793
56
- nedo_vision_worker_core/models/worker_source_pipeline.py,sha256=xCD4i9pHr8Qy5B_h1dH0Q7V7faS2lAou2UNEzx24oIw,752
56
+ nedo_vision_worker_core/models/worker_source_pipeline.py,sha256=CGA_nz5wywsJcBPm-5kd0v_-h59f8Iu7uEeX3C91eT4,824
57
57
  nedo_vision_worker_core/models/worker_source_pipeline_config.py,sha256=dGYTpcTFFu6pmGBufuWBHjv3Xs4RGAQwZn6jp6Ondvs,876
58
58
  nedo_vision_worker_core/models/worker_source_pipeline_debug.py,sha256=6S7TkN37FrAT4VwsEB38DWSad7QfvNhsOGtSEK8D1Qs,594
59
59
  nedo_vision_worker_core/models/worker_source_pipeline_detection.py,sha256=p6CJsiVCKprTYrNxJsiTB8njXdHkjZKVEyBceRVE6fY,560
@@ -61,7 +61,7 @@ nedo_vision_worker_core/pipeline/ModelManager.py,sha256=K7lmVOo-KL7bnWtyafilZs23
61
61
  nedo_vision_worker_core/pipeline/PipelineConfigManager.py,sha256=X55i9GyXcW9ylO6cj2UMAZFSxxPViacL4H4DZl60CAY,1157
62
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=KMdFDvQO2yI9NxjNDwRQU07St1Y01QgM97tgNuyelw0,26766
64
+ nedo_vision_worker_core/pipeline/PipelineProcessor.py,sha256=E3W9E1Ph_Qr6Gabdk3AUCtAIHw-r2-opJ0RuwOJlziI,29288
65
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
@@ -94,11 +94,12 @@ nedo_vision_worker_core/util/DrawingUtils.py,sha256=sLptmzVaJakP_ZgbZsLL03RMH_9N
94
94
  nedo_vision_worker_core/util/ModelReadinessChecker.py,sha256=ywHvt_d7UlY3DyFEJrO4Iyl0zx3SaLKb-Qab5l5Q8n4,6548
95
95
  nedo_vision_worker_core/util/PersonAttributeMatcher.py,sha256=PhYTPYSF62Nfuc7dage03K6icw_bBBdpvXvnlzCbS30,2773
96
96
  nedo_vision_worker_core/util/PersonRestrictedAreaMatcher.py,sha256=iuzCU32BQKaZ3dIy0QHNg2yoWJA-XhTRwwYqCvFdDgg,1711
97
+ nedo_vision_worker_core/util/PipelinePreviewChecker.py,sha256=XxlSMlrDlRrzfV8_Y--40Xfk5N7FjGgkKHth3KKCZzU,1963
97
98
  nedo_vision_worker_core/util/PlatformDetector.py,sha256=GGL8UfeMQITR22EMYIRWnuOEnSqo7Dr5mb0PaFrl8AM,3006
98
99
  nedo_vision_worker_core/util/TablePrinter.py,sha256=wzLGgb1GFMeIbAP6HmKcZD33j4D-IlyqlyeR7C5yD7w,1137
99
100
  nedo_vision_worker_core/util/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
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,,
101
+ nedo_vision_worker_core-0.3.7.dist-info/METADATA,sha256=fG5OjnALiAwUrLmyQXrQAu9FyvxiiVdsDqKrNSk2e68,14412
102
+ nedo_vision_worker_core-0.3.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
+ nedo_vision_worker_core-0.3.7.dist-info/entry_points.txt,sha256=pIPafsvPnBw-fpBKBmc1NQCQ6PQY3ad8mZ6mn8_p5FI,70
104
+ nedo_vision_worker_core-0.3.7.dist-info/top_level.txt,sha256=y8kusXjVYqtG8MSHYWTrk8bRrvjOrphKXYyzu943TTQ,24
105
+ nedo_vision_worker_core-0.3.7.dist-info/RECORD,,