matrice-streaming 0.1.74__tar.gz → 0.1.76__tar.gz

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 (75) hide show
  1. matrice_streaming-0.1.76/MANIFEST.in +4 -0
  2. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/PKG-INFO +5 -4
  3. matrice_streaming-0.1.76/build-config.json +18 -0
  4. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/pyproject.toml +23 -7
  5. matrice_streaming-0.1.76/setup.py +87 -0
  6. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/client/client.py +6 -5
  7. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/client/client_utils.py +6 -5
  8. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/deployment/camera_manager.py +46 -45
  9. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/deployment/deployment.py +9 -8
  10. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/deployment/inference_pipeline.py +14 -13
  11. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/deployment/streaming_gateway_manager.py +15 -11
  12. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/__init__.py +32 -9
  13. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/async_camera_worker.py +43 -41
  14. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/async_ffmpeg_worker.py +39 -37
  15. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/camera_streamer.py +9 -7
  16. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/device_detection.py +8 -8
  17. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/encoder_manager.py +5 -3
  18. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/encoding_pool_manager.py +2 -2
  19. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_camera_streamer.py +8 -6
  20. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_config.py +2 -0
  21. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_worker_manager.py +4 -4
  22. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/frame_processor.py +2 -0
  23. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_camera_streamer.py +13 -11
  24. matrice_streaming-0.1.76/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_rtp_demuxer.py +763 -0
  25. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker.py +39 -37
  26. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker_manager.py +3 -1
  27. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/message_builder.py +2 -0
  28. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/nvdec.py +470 -118
  29. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/nvdec_worker_manager.py +80 -41
  30. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/platform_pipelines.py +3 -1
  31. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/retry_manager.py +8 -6
  32. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/stream_statistics.py +20 -18
  33. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/video_capture_manager.py +12 -12
  34. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py +7 -5
  35. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/dynamic_camera_manager.py +506 -38
  36. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/event_listener.py +2 -0
  37. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/metrics_reporter.py +14 -13
  38. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/streaming_action.py +21 -17
  39. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/streaming_gateway.py +39 -40
  40. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/streaming_gateway_utils.py +9 -7
  41. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/streaming_status_listener.py +2 -0
  42. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76/src}/matrice_streaming.egg-info/PKG-INFO +5 -4
  43. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76/src}/matrice_streaming.egg-info/SOURCES.txt +7 -8
  44. matrice_streaming-0.1.74/matrice_streaming.egg-info/not-zip-safe +0 -1
  45. matrice_streaming-0.1.74/setup.py +0 -322
  46. matrice_streaming-0.1.74/src/matrice_streaming/deployment/todo.txt +0 -1
  47. matrice_streaming-0.1.74/src/matrice_streaming/streaming_gateway/camera_streamer/ARCHITECTURE.md +0 -324
  48. matrice_streaming-0.1.74/src/matrice_streaming/streaming_gateway/debug/README.md +0 -486
  49. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/LICENSE.txt +0 -0
  50. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/README.md +0 -0
  51. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/setup.cfg +0 -0
  52. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/__init__.py +0 -0
  53. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/client/__init__.py +0 -0
  54. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/deployment/__init__.py +0 -0
  55. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/py.typed +0 -0
  56. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/__init__.py +0 -0
  57. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/__init__.py +0 -0
  58. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/benchmark.py +0 -0
  59. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/debug_gstreamer_gateway.py +0 -0
  60. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/debug_stream_backend.py +0 -0
  61. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/debug_streaming_gateway.py +0 -0
  62. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/debug_utils.py +0 -0
  63. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/example_debug_streaming.py +0 -0
  64. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/src/matrice_streaming/streaming_gateway/debug/test_videoplayback.py +0 -0
  65. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76/src}/matrice_streaming.egg-info/dependency_links.txt +0 -0
  66. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76/src}/matrice_streaming.egg-info/top_level.txt +0 -0
  67. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_async_infrastructure.py +0 -0
  68. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_batch_auto_calculation.py +0 -0
  69. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_batching_verification.py +0 -0
  70. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_e2e_production.py +0 -0
  71. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_flatten_binary.py +0 -0
  72. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_gstreamer_integration.py +0 -0
  73. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_msgpack_fix.py +0 -0
  74. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_phase1_unit.py +0 -0
  75. {matrice_streaming-0.1.74 → matrice_streaming-0.1.76}/tests/test_phase2_scaling.py +0 -0
@@ -0,0 +1,4 @@
1
+ include LICENSE.txt
2
+ include README.md
3
+ include build-config.json
4
+ recursive-include src *.py *.pyi py.typed
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
- Name: matrice_streaming
3
- Version: 0.1.74
2
+ Name: matrice-streaming
3
+ Version: 0.1.76
4
4
  Summary: Common server utilities for Matrice.ai services
5
5
  Author-email: "Matrice.ai" <dipendra@matrice.ai>
6
- License-Expression: MIT
7
- Keywords: matrice,common,utilities,pyarmor,obfuscated
6
+ License: MIT
7
+ Keywords: matrice,streaming,video,utilities
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: Operating System :: OS Independent
@@ -12,6 +12,7 @@ Classifier: Operating System :: POSIX :: Linux
12
12
  Classifier: Operating System :: Microsoft :: Windows
13
13
  Classifier: Operating System :: MacOS
14
14
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Classifier: Programming Language :: Python :: 3
15
16
  Classifier: Programming Language :: Python :: 3.8
16
17
  Classifier: Programming Language :: Python :: 3.9
17
18
  Classifier: Programming Language :: Python :: 3.10
@@ -0,0 +1,18 @@
1
+ {
2
+ "versions": {
3
+ "test_pypi_dev": "0.1.69",
4
+ "test_pypi": "0.1.58",
5
+ "pypi": "0.1.76"
6
+ },
7
+ "build": {
8
+ "enable_mypyc": true,
9
+ "python_versions": ["3.8", "3.9", "3.10", "3.11", "3.12"]
10
+ },
11
+ "platforms": {
12
+ "linux_x86_64": true,
13
+ "linux_arm64": false,
14
+ "windows_x64": false,
15
+ "macos_x64": false,
16
+ "macos_arm64": false
17
+ }
18
+ }
@@ -1,16 +1,22 @@
1
1
  [build-system]
2
- requires = ["setuptools>=65.0.0", "wheel", "pyarmor>=8.0.0"]
2
+ requires = [
3
+ "setuptools>=65.0.0",
4
+ "wheel",
5
+ "mypy[mypyc]>=1.0",
6
+ "types-requests",
7
+ "types-python-dateutil",
8
+ ]
3
9
  build-backend = "setuptools.build_meta"
4
10
 
5
11
  [project]
6
- name = "matrice_streaming"
12
+ name = "matrice-streaming"
7
13
  dynamic = ["version"]
8
14
  description = "Common server utilities for Matrice.ai services"
9
15
  readme = "README.md"
10
16
  authors = [{ name = "Matrice.ai", email = "dipendra@matrice.ai" }]
11
- license = "MIT"
12
- requires-python = ">=3.8, <4"
13
- keywords = ["matrice", "common", "utilities", "pyarmor", "obfuscated"]
17
+ license = {text = "MIT"}
18
+ requires-python = ">=3.8"
19
+ keywords = ["matrice", "streaming", "video", "utilities"]
14
20
  classifiers = [
15
21
  "Development Status :: 4 - Beta",
16
22
  "Intended Audience :: Developers",
@@ -19,6 +25,7 @@ classifiers = [
19
25
  "Operating System :: Microsoft :: Windows",
20
26
  "Operating System :: MacOS",
21
27
  "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Programming Language :: Python :: 3",
22
29
  "Programming Language :: Python :: 3.8",
23
30
  "Programming Language :: Python :: 3.9",
24
31
  "Programming Language :: Python :: 3.10",
@@ -26,7 +33,16 @@ classifiers = [
26
33
  "Programming Language :: Python :: 3.12",
27
34
  "Typing :: Typed",
28
35
  ]
36
+ dependencies = []
37
+
38
+ [tool.setuptools]
39
+ package-dir = {"" = "src"}
29
40
 
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
30
43
 
31
- # PyArmor configuration is handled via pyarmor.yml file
32
- # The [tool.pyarmor] section is not used in PyArmor 8.x
44
+ [tool.mypy]
45
+ follow_imports = "skip"
46
+ ignore_missing_imports = true
47
+ warn_unused_ignores = false
48
+ show_error_codes = true
@@ -0,0 +1,87 @@
1
+ """Setup script with optional mypyc compilation."""
2
+ import os
3
+ import glob
4
+ from setuptools import setup, find_packages
5
+ from typing import List
6
+
7
+ # =============================================================================
8
+ # CONFIGURATION
9
+ # =============================================================================
10
+
11
+ # Environment variables control build behavior
12
+ ENABLE_MYPYC = os.environ.get("ENABLE_MYPYC", "").lower() in ("true", "1", "yes")
13
+
14
+ # Source directory
15
+ SRC_DIR = "src"
16
+ PACKAGE_NAME = "matrice_streaming"
17
+
18
+
19
+ def get_version() -> str:
20
+ """Get version from environment or default."""
21
+ return os.environ.get("PACKAGE_VERSION", "0.0.0.dev0")
22
+
23
+
24
+ def discover_modules() -> List[str]:
25
+ """Discover all Python modules for mypyc compilation."""
26
+ modules = []
27
+ package_dir = os.path.join(SRC_DIR, PACKAGE_NAME)
28
+
29
+ for py_file in glob.glob(f"{package_dir}/**/*.py", recursive=True):
30
+ # Skip __init__.py, test files, and debug files
31
+ basename = os.path.basename(py_file)
32
+ if basename.startswith("_") or "test" in basename.lower():
33
+ continue
34
+ # Skip debug directory (not performance critical)
35
+ if "debug" in py_file.replace("\\", "/"):
36
+ continue
37
+ modules.append(py_file)
38
+
39
+ return modules
40
+
41
+
42
+ def get_ext_modules():
43
+ """Get extension modules for mypyc compilation."""
44
+ if not ENABLE_MYPYC:
45
+ print("=" * 60)
46
+ print("Building PURE PYTHON package (mypyc disabled)")
47
+ print("=" * 60)
48
+ return []
49
+
50
+ try:
51
+ from mypyc.build import mypycify
52
+ except ImportError:
53
+ print("WARNING: mypyc not available, building pure Python package")
54
+ return []
55
+
56
+ print("=" * 60)
57
+ print("Building MYPYC COMPILED package")
58
+ print("=" * 60)
59
+
60
+ modules = discover_modules()
61
+ print(f"Compiling {len(modules)} modules with mypyc")
62
+
63
+ mypyc_options = [
64
+ "--follow-imports=skip",
65
+ "--ignore-missing-imports",
66
+ "--disable-error-code=annotation-unchecked",
67
+ ]
68
+
69
+ return mypycify(mypyc_options + modules, opt_level="3")
70
+
71
+
72
+ # =============================================================================
73
+ # SETUP
74
+ # =============================================================================
75
+
76
+ setup(
77
+ name=PACKAGE_NAME.replace("_", "-"),
78
+ version=get_version(),
79
+ packages=find_packages(where=SRC_DIR),
80
+ package_dir={"": SRC_DIR},
81
+ ext_modules=get_ext_modules(),
82
+ python_requires=">=3.8",
83
+ include_package_data=True,
84
+ package_data={
85
+ PACKAGE_NAME: ["py.typed", "**/*.pyi"],
86
+ },
87
+ )
@@ -1,4 +1,5 @@
1
1
  """Module providing client functionality."""
2
+ from __future__ import annotations
2
3
 
3
4
  import time
4
5
  import logging
@@ -40,8 +41,8 @@ class MatriceDeployClient:
40
41
  self,
41
42
  session,
42
43
  deployment_id: str,
43
- auth_key: str = None,
44
- create_deployment_config: Dict = None,
44
+ auth_key: Optional[str] = None,
45
+ create_deployment_config: Optional[Dict] = None,
45
46
  ):
46
47
  """Initialize MatriceDeployClient.
47
48
 
@@ -69,7 +70,7 @@ class MatriceDeployClient:
69
70
  self.rpc = self.session.rpc
70
71
  self.deployment_id = deployment_id
71
72
  self.auth_key = auth_key
72
- self.index_to_category = {}
73
+ self.index_to_category = {} # type: ignore[var-annotated]
73
74
  self.last_refresh_time = time.time()
74
75
  self.create_deployment_config = create_deployment_config
75
76
 
@@ -177,7 +178,7 @@ class MatriceDeployClient:
177
178
  )
178
179
  if wait_for_deployment:
179
180
  self.wait_for_deployment(max_wait_time)
180
- self.__init__(
181
+ self.__init__( # type: ignore[misc]
181
182
  session=self.session,
182
183
  deployment_id=deployment_id,
183
184
  )
@@ -224,7 +225,7 @@ class MatriceDeployClient:
224
225
  )
225
226
  if resp.get("success"):
226
227
  self.auth_key = resp["data"]["key"]
227
- return self.auth_key
228
+ return self.auth_key # type: ignore[return-value]
228
229
  else:
229
230
  raise RuntimeError(f"Failed to create auth key: {resp}")
230
231
  except Exception as exc:
@@ -1,4 +1,5 @@
1
1
  """Module providing client_utils functionality."""
2
+ from __future__ import annotations
2
3
 
3
4
  import json
4
5
  import logging
@@ -15,7 +16,7 @@ import httpx
15
16
  class ClientUtils:
16
17
  """Utility class for making inference requests to model servers."""
17
18
 
18
- def __init__(self, clients: List[Dict] = None):
19
+ def __init__(self, clients: Optional[List[Dict]] = None):
19
20
  """Initialize HTTP clients."""
20
21
  self.http_client = httpx.Client(timeout=360, follow_redirects=True)
21
22
  self.async_client = httpx.AsyncClient(timeout=360, follow_redirects=True)
@@ -62,7 +63,7 @@ class ClientUtils:
62
63
 
63
64
  def _prepare_request_data(
64
65
  self,
65
- auth_key: str = None,
66
+ auth_key: Optional[str] = None,
66
67
  input_path: Optional[str] = None,
67
68
  input_bytes: Optional[bytes] = None,
68
69
  input_url: Optional[str] = None,
@@ -96,7 +97,7 @@ class ClientUtils:
96
97
  if input_path:
97
98
  files["input"] = open(input_path, "rb")
98
99
  elif input_bytes:
99
- files["input"] = input_bytes
100
+ files["input"] = input_bytes # type: ignore[assignment]
100
101
 
101
102
  data = {"auth_key": auth_key, "apply_post_processing": str(apply_post_processing).lower()}
102
103
  if input_url:
@@ -302,7 +303,7 @@ class ClientUtils:
302
303
 
303
304
  def inference(
304
305
  self,
305
- auth_key: str = None,
306
+ auth_key: Optional[str] = None,
306
307
  input_path: Optional[str] = None,
307
308
  input_bytes: Optional[bytes] = None,
308
309
  input_url: Optional[str] = None,
@@ -357,7 +358,7 @@ class ClientUtils:
357
358
 
358
359
  async def async_inference(
359
360
  self,
360
- auth_key: str = None,
361
+ auth_key: Optional[str] = None,
361
362
  input_path: Optional[str] = None,
362
363
  input_bytes: Optional[bytes] = None,
363
364
  input_url: Optional[str] = None,
@@ -1,4 +1,5 @@
1
1
  """Module providing camera manager functionality for deployments."""
2
+ from __future__ import annotations
2
3
 
3
4
  import logging
4
5
  from typing import Dict, List, Optional, Tuple
@@ -77,7 +78,7 @@ class CameraLocationConfig:
77
78
  if self.account_number:
78
79
  data["accountNumber"] = self.account_number
79
80
  if self.location_info:
80
- data["locationInfo"] = self.location_info.to_dict()
81
+ data["locationInfo"] = self.location_info.to_dict() # type: ignore[assignment]
81
82
  if self.id:
82
83
  data["_id"] = self.id
83
84
 
@@ -217,7 +218,7 @@ class CameraGroupConfig:
217
218
  id=data.get("_id") or data.get("id") or data.get("ID"),
218
219
  camera_group_name=data.get("cameraGroupName") or data.get("name") or data.get("Name") or "",
219
220
  streaming_gateway_id=data.get("streamingGatewayId") or data.get("idService") or data.get("IDService") or "",
220
- default_stream_settings=default_settings,
221
+ default_stream_settings=default_settings, # type: ignore[arg-type]
221
222
  account_number=data.get("accountNumber"),
222
223
  location_id=data.get("locationId"),
223
224
  created_at=data.get("createdAt") or data.get("CreatedAt"),
@@ -283,7 +284,7 @@ class CameraConfig:
283
284
  if self.simulation_video_path:
284
285
  data["simulationVideoPath"] = self.simulation_video_path
285
286
  if self.custom_stream_settings:
286
- data["customStreamSettings"] = self.custom_stream_settings
287
+ data["customStreamSettings"] = self.custom_stream_settings # type: ignore[assignment]
287
288
  if self.id:
288
289
  data["_id"] = self.id
289
290
 
@@ -301,7 +302,7 @@ class CameraConfig:
301
302
 
302
303
  instance = cls(
303
304
  id=data.get("ID") or data.get("id") or data.get("_id"),
304
- camera_group_id=camera_group_id,
305
+ camera_group_id=camera_group_id, # type: ignore[arg-type]
305
306
  camera_name=data.get("CameraName") or data.get("cameraName") or "",
306
307
  protocol_type=data.get("protocolType") or data.get("ProtocolType") or "RTSP",
307
308
  camera_feed_path=data.get("cameraFeedPath") or data.get("CameraFeedPath"),
@@ -351,7 +352,7 @@ class CameraConfig:
351
352
  }
352
353
 
353
354
  for api_key, attr_name in custom_mapping.items():
354
- if api_key in self.custom_stream_settings and self.custom_stream_settings[api_key]:
355
+ if self.custom_stream_settings and api_key in self.custom_stream_settings and self.custom_stream_settings[api_key]:
355
356
  effective[attr_name] = self.custom_stream_settings[api_key]
356
357
 
357
358
  return StreamSettings(**effective)
@@ -394,7 +395,7 @@ class Camera:
394
395
  ```
395
396
  """
396
397
 
397
- def __init__(self, session, config: CameraConfig = None, camera_id: str = None):
398
+ def __init__(self, session, config: Optional[CameraConfig] = None, camera_id: Optional[str] = None):
398
399
  """
399
400
  Initialize a Camera instance.
400
401
 
@@ -467,7 +468,7 @@ class Camera:
467
468
  self.config.camera_group_id = value
468
469
 
469
470
  @property
470
- def custom_stream_settings(self) -> Dict:
471
+ def custom_stream_settings(self) -> Optional[Dict]:
471
472
  """Get the custom stream settings."""
472
473
  return self.config.custom_stream_settings if self.config else {}
473
474
 
@@ -492,7 +493,7 @@ class Camera:
492
493
  error_msg = resp.get("message") if resp else "No response received"
493
494
  raise ValueError(f"Failed to load camera: {error_msg}")
494
495
 
495
- def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]:
496
+ def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]: # type: ignore[assignment]
496
497
  """
497
498
  Save the camera configuration to the backend (create new).
498
499
 
@@ -554,7 +555,7 @@ class Camera:
554
555
  if self.config.simulation_video_path:
555
556
  payload["simulationVideoPath"] = self.config.simulation_video_path
556
557
  if self.config.custom_stream_settings:
557
- payload["customStreamSettings"] = self.config.custom_stream_settings
558
+ payload["customStreamSettings"] = self.config.custom_stream_settings # type: ignore[assignment]
558
559
 
559
560
  resp = self.rpc.put(path=path, payload=payload)
560
561
 
@@ -588,17 +589,17 @@ class Camera:
588
589
 
589
590
  def get_stream_url(self) -> str:
590
591
  """Get the camera stream URL."""
591
- if not self.config.id:
592
+ if not self.config or not self.config.id:
592
593
  return ""
593
594
  if self.config.is_stream_url:
594
- return self.config.stream_url
595
+ return self.config.stream_url or ""
595
596
 
596
597
  resp = self.rpc.get(f"/v1/inference/get_stream_url/{self.config.id}")
597
598
  if resp and resp.get("success") and resp.get("data"):
598
599
  self.config.stream_url = resp.get("data", {}).get("streamUrl")
599
600
  self.config.is_stream_url = True
600
601
 
601
- return self.config.stream_url
602
+ return self.config.stream_url or ""
602
603
 
603
604
  def get_effective_stream_settings(
604
605
  self, group_defaults: StreamSettings
@@ -729,7 +730,7 @@ class CameraGroup:
729
730
  ```
730
731
  """
731
732
 
732
- def __init__(self, session, config: CameraGroupConfig = None, group_id: str = None):
733
+ def __init__(self, session, config: Optional[CameraGroupConfig] = None, group_id: Optional[str] = None):
733
734
  """
734
735
  Initialize a CameraGroup.
735
736
 
@@ -743,7 +744,7 @@ class CameraGroup:
743
744
 
744
745
  self.session = session
745
746
  self.rpc = session.rpc
746
- self._cameras = [] # Cache for cameras in this group
747
+ self._cameras: list = [] # Cache for cameras in this group
747
748
 
748
749
  if group_id:
749
750
  # Load existing group
@@ -843,7 +844,7 @@ class CameraGroup:
843
844
  error_msg = resp.get("message") if resp else "No response received"
844
845
  raise ValueError(f"Failed to load camera group: {error_msg}")
845
846
 
846
- def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]:
847
+ def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]: # type: ignore[assignment]
847
848
  """
848
849
  Save the camera group configuration to the backend (create new).
849
850
 
@@ -901,9 +902,9 @@ class CameraGroup:
901
902
  path = f"/v1/inference/update_camera_group/{self.config.id}"
902
903
  payload = {
903
904
  "cameraGroupName": self.config.camera_group_name,
904
- "defaultStreamSettings": self.config.default_stream_settings.to_dict()
905
+ "defaultStreamSettings": self.config.default_stream_settings.to_dict() # type: ignore[union-attr]
905
906
  }
906
-
907
+
907
908
  if self.config.location_id:
908
909
  payload["locationId"] = self.config.location_id
909
910
 
@@ -978,7 +979,7 @@ class CameraGroup:
978
979
  return camera_instance, None, message
979
980
 
980
981
  def get_cameras(
981
- self, page: int = 1, limit: int = 10, search: str = None
982
+ self, page: int = 1, limit: int = 10, search: str = None # type: ignore[assignment]
982
983
  ) -> Tuple[Optional[List["Camera"]], Optional[str], str]:
983
984
  """
984
985
  Get all cameras in this camera group.
@@ -1159,7 +1160,7 @@ class CameraLocation:
1159
1160
  ```
1160
1161
  """
1161
1162
 
1162
- def __init__(self, session, config: CameraLocationConfig = None, location_id: str = None):
1163
+ def __init__(self, session, config: Optional[CameraLocationConfig] = None, location_id: Optional[str] = None):
1163
1164
  """
1164
1165
  Initialize a CameraLocation instance.
1165
1166
 
@@ -1235,7 +1236,7 @@ class CameraLocation:
1235
1236
  error_msg = resp.get("message") if resp else "No response received"
1236
1237
  raise ValueError(f"Failed to load location: {error_msg}")
1237
1238
 
1238
- def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]:
1239
+ def save(self, account_number: str = None) -> Tuple[Optional[Dict], Optional[str], str]: # type: ignore[assignment]
1239
1240
  """
1240
1241
  Save the location configuration to the backend (create new).
1241
1242
 
@@ -1287,7 +1288,7 @@ class CameraLocation:
1287
1288
  }
1288
1289
 
1289
1290
  if self.config.location_info:
1290
- payload["locationInfo"] = self.config.location_info.to_dict()
1291
+ payload["locationInfo"] = self.config.location_info.to_dict() # type: ignore[assignment]
1291
1292
 
1292
1293
  resp = self.rpc.put(path=path, payload=payload)
1293
1294
 
@@ -1383,7 +1384,7 @@ class CameraManager:
1383
1384
  ```
1384
1385
  """
1385
1386
 
1386
- def __init__(self, session, service_id: str = None, account_number: str = None):
1387
+ def __init__(self, session, service_id: Optional[str] = None, account_number: Optional[str] = None):
1387
1388
  """
1388
1389
  Initialize the CameraManager client.
1389
1390
 
@@ -1430,7 +1431,7 @@ class CameraManager:
1430
1431
  location = CameraLocation(self.session, config)
1431
1432
 
1432
1433
  # Save to backend
1433
- result, error, message = location.save(account_number=self.account_number)
1434
+ result, error, message = location.save(account_number=self.account_number) # type: ignore[arg-type]
1434
1435
 
1435
1436
  if error:
1436
1437
  return None, error, message
@@ -1594,7 +1595,7 @@ class CameraManager:
1594
1595
  }
1595
1596
 
1596
1597
  if config.location_info:
1597
- payload["locationInfo"] = config.location_info.to_dict()
1598
+ payload["locationInfo"] = config.location_info.to_dict() # type: ignore[assignment]
1598
1599
 
1599
1600
  resp = self.rpc.put(path=path, payload=payload)
1600
1601
  return self.handle_response(
@@ -1630,10 +1631,10 @@ class CameraManager:
1630
1631
  topic_name: str,
1631
1632
  topic_type: str,
1632
1633
  status: str,
1633
- ip_address: str = None,
1634
- port: int = None,
1635
- app_deployment_id: str = None,
1636
- consuming_app_deployment_ids: List[str] = None
1634
+ ip_address: Optional[str] = None,
1635
+ port: Optional[int] = None,
1636
+ app_deployment_id: Optional[str] = None,
1637
+ consuming_app_deployment_ids: Optional[List[str]] = None
1637
1638
  ) -> Tuple[Optional[Dict], Optional[str], str]:
1638
1639
  """
1639
1640
  Create a camera stream topic.
@@ -1798,7 +1799,7 @@ class CameraManager:
1798
1799
  camera_group_instance = CameraGroup(self.session, group)
1799
1800
 
1800
1801
  # Save to backend
1801
- result, error, message = camera_group_instance.save(account_number=self.account_number)
1802
+ result, error, message = camera_group_instance.save(account_number=self.account_number) # type: ignore[arg-type]
1802
1803
 
1803
1804
  if error:
1804
1805
  return None, error, message
@@ -1827,7 +1828,7 @@ class CameraManager:
1827
1828
  return None, str(e), "Failed to retrieve camera group"
1828
1829
 
1829
1830
  def get_camera_groups(
1830
- self, page: int = 1, limit: int = 10
1831
+ self, page: int = 1, limit: int = 10, search: Optional[str] = None
1831
1832
  ) -> Tuple[Optional[List["CameraGroup"]], Optional[str], str]:
1832
1833
  """
1833
1834
  Get all camera groups for the account.
@@ -1972,9 +1973,9 @@ class CameraManager:
1972
1973
  path = f"/v1/inference/update_camera_group/{group_id}"
1973
1974
  payload = {
1974
1975
  "cameraGroupName": group.camera_group_name,
1975
- "defaultStreamSettings": group.default_stream_settings.to_dict()
1976
+ "defaultStreamSettings": group.default_stream_settings.to_dict() # type: ignore[union-attr]
1976
1977
  }
1977
-
1978
+
1978
1979
  if group.location_id:
1979
1980
  payload["locationId"] = group.location_id
1980
1981
 
@@ -2026,10 +2027,10 @@ class CameraManager:
2026
2027
  camera_instance = Camera(self.session, camera_config)
2027
2028
 
2028
2029
  # Save to backend
2029
- result, error, message = camera_instance.save(account_number=self.account_number)
2030
+ result, error, message = camera_instance.save(account_number=self.account_number) # type: ignore[arg-type]
2030
2031
 
2031
2032
  if error:
2032
- return result, error, message
2033
+ return result, error, message # type: ignore[return-value]
2033
2034
 
2034
2035
  return camera_instance, None, message
2035
2036
 
@@ -2055,7 +2056,7 @@ class CameraManager:
2055
2056
  return None, str(e), "Failed to retrieve camera"
2056
2057
 
2057
2058
  def list_camera_configs(
2058
- self, page: int = 1, limit: int = 10, search: str = None, group_id: str = None
2059
+ self, page: int = 1, limit: int = 10, search: Optional[str] = None, group_id: Optional[str] = None
2059
2060
  ) -> Tuple[Optional[List[Dict]], Optional[str], str]:
2060
2061
  """
2061
2062
  List all camera configs for account (updated to use account-based approach).
@@ -2069,13 +2070,13 @@ class CameraManager:
2069
2070
  # Use account-based camera stream endpoint
2070
2071
  if group_id:
2071
2072
  # Get cameras for specific group using new API
2072
- return self.get_cameras(group_id=group_id, limit=limit)
2073
+ return self.get_cameras(group_id=group_id, limit=limit) # type: ignore[return-value]
2073
2074
  else:
2074
2075
  # Get all cameras for account using paginated endpoint
2075
2076
  path = f"/v1/inference/all_camera_streams_pag/{self.account_number}"
2076
2077
  params = {"page": page, "limit": limit}
2077
2078
  if search:
2078
- params["search"] = search
2079
+ params["search"] = search # type: ignore[assignment]
2079
2080
 
2080
2081
  resp = self.rpc.get(path=path, params=params)
2081
2082
 
@@ -2101,7 +2102,7 @@ class CameraManager:
2101
2102
  return [], None, message
2102
2103
 
2103
2104
  def get_cameras(
2104
- self, page: int = 1, limit: int = 10, search: str = None, group_id: str = None
2105
+ self, page: int = 1, limit: int = 10, search: Optional[str] = None, group_id: Optional[str] = None
2105
2106
  ) -> Tuple[Optional[List["Camera"]], Optional[str], str]:
2106
2107
  """
2107
2108
  Get all cameras for a specific deployment.
@@ -2130,7 +2131,7 @@ class CameraManager:
2130
2131
  return None, error, message
2131
2132
 
2132
2133
  camera_instances = []
2133
- for config_data in cameras:
2134
+ for config_data in cameras: # type: ignore[union-attr]
2134
2135
  try:
2135
2136
  camera_config = CameraConfig.from_dict(config_data)
2136
2137
  if group_id and camera_config.camera_group_id != group_id:
@@ -2167,7 +2168,7 @@ class CameraManager:
2167
2168
  result, error, message = self.handle_response(
2168
2169
  resp, "Stream URL retrieved successfully", "Failed to retrieve stream URL"
2169
2170
  )
2170
- return result, error, message
2171
+ return result, error, message # type: ignore[return-value]
2171
2172
 
2172
2173
  def update_camera(
2173
2174
  self, camera_id: str, camera_config: CameraConfig
@@ -2264,7 +2265,7 @@ class CameraManager:
2264
2265
  # Fallback to direct deletion if Camera object doesn't have delete method
2265
2266
  camera_id = getattr(camera, 'id', None)
2266
2267
  if camera_id:
2267
- result, del_error, del_message = self.delete_camera_by_id(camera_id)
2268
+ result, del_error, del_message = self.delete_camera_by_id(camera_id) # type: ignore[attr-defined]
2268
2269
  if del_error:
2269
2270
  failed_deletions.append(f"Camera {camera_id}: {del_error}")
2270
2271
  else:
@@ -2340,9 +2341,9 @@ class CameraManager:
2340
2341
  return None, error_msg, "Failed to create any cameras"
2341
2342
  else:
2342
2343
  # Partial success
2343
- return created_cameras, error_msg, f"Created {len(created_cameras)} cameras with some failures"
2344
+ return created_cameras, error_msg, f"Created {len(created_cameras)} cameras with some failures" # type: ignore[return-value]
2344
2345
 
2345
- return created_cameras, None, f"Successfully created {len(created_cameras)} cameras"
2346
+ return created_cameras, None, f"Successfully created {len(created_cameras)} cameras" # type: ignore[return-value]
2346
2347
 
2347
2348
  def _validate_camera_group(self, group: CameraGroupConfig) -> Tuple[bool, str]:
2348
2349
  """
@@ -2443,7 +2444,7 @@ class CameraManager:
2443
2444
  return self.get_camera_by_id(config_id)
2444
2445
 
2445
2446
  def get_camera_configs(
2446
- self, page: int = 1, limit: int = 10, search: str = None, group_id: str = None
2447
+ self, page: int = 1, limit: int = 10, search: Optional[str] = None, group_id: Optional[str] = None
2447
2448
  ) -> Tuple[Optional[List["Camera"]], Optional[str], str]:
2448
2449
  """Legacy method - use get_cameras instead."""
2449
2450
  return self.get_cameras(page, limit, search, group_id)
@@ -2482,4 +2483,4 @@ class CameraManager:
2482
2483
  if error:
2483
2484
  return None, error, message
2484
2485
 
2485
- return {"cameras": [cam.config.to_dict() for cam in cameras]}, None, message
2486
+ return {"cameras": [cam.config.to_dict() for cam in cameras]}, None, message # type: ignore[union-attr]