python-aidot-cameras 0.12.11__tar.gz → 0.12.12__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.
- {python_aidot_cameras-0.12.11/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.12}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/client.py +29 -2
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/pyproject.toml +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
- python_aidot_cameras-0.12.12/tests/test_viewer_check_throttle.py +71 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/LICENSE +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/README.md +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/setup.cfg +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_wait_or_event.py +0 -0
{python_aidot_cameras-0.12.11/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.12}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.12
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/client.py
RENAMED
|
@@ -148,6 +148,13 @@ _BG_TASKS: set = set()
|
|
|
148
148
|
# means refetching is not helping, so back off rather than loop.
|
|
149
149
|
_MQTT_CREDENTIAL_REFETCH_FLOOR = 30.0
|
|
150
150
|
|
|
151
|
+
# How often the "is anyone watching" question may actually reach go2rtc. The
|
|
152
|
+
# watchdog loops tick twice a second; asking that often opens a fresh HTTP
|
|
153
|
+
# session per camera per tick against the service that also has to serve the
|
|
154
|
+
# video. Idle release is measured in minutes, so seconds of staleness here costs
|
|
155
|
+
# nothing.
|
|
156
|
+
_VIEWER_CHECK_INTERVAL_S = 10.0
|
|
157
|
+
|
|
151
158
|
|
|
152
159
|
def _spawn_bg(coro):
|
|
153
160
|
_t = asyncio.ensure_future(coro)
|
|
@@ -2937,6 +2944,10 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2937
2944
|
register, deregister and viewer-count paths cannot drift apart."""
|
|
2938
2945
|
return f"aidot_{self.device_id[:12]}"
|
|
2939
2946
|
|
|
2947
|
+
# Cached answer to "is anyone watching", so the watchdog loops can ask on
|
|
2948
|
+
# every tick without hammering go2rtc.
|
|
2949
|
+
_viewer_cache: "tuple[float, Optional[bool]]" = (0.0, None)
|
|
2950
|
+
|
|
2940
2951
|
async def _viewer_present(self, port: int) -> "Optional[bool]":
|
|
2941
2952
|
"""Is anyone ACTUALLY watching? True / False / None (unknown).
|
|
2942
2953
|
|
|
@@ -2952,6 +2963,17 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2952
2963
|
Falls back to the TCP-table check when go2rtc is not in use, and returns
|
|
2953
2964
|
None (never release) if neither can answer.
|
|
2954
2965
|
"""
|
|
2966
|
+
# THROTTLE FIRST. The DTLS watchdog calls this every 0.5s and the SDES
|
|
2967
|
+
# one on a similar cadence, and the go2rtc query below opens a fresh HTTP
|
|
2968
|
+
# session each time - so an unthrottled check is two requests per second
|
|
2969
|
+
# per camera, forever, against the very service that has to serve the
|
|
2970
|
+
# video. On a small fleet that is enough load to stop go2rtc answering
|
|
2971
|
+
# at all, which looks exactly like "no video anywhere".
|
|
2972
|
+
_now_mono = time.monotonic()
|
|
2973
|
+
_last, _cached = self._viewer_cache
|
|
2974
|
+
if _now_mono - _last < _VIEWER_CHECK_INTERVAL_S:
|
|
2975
|
+
return _cached
|
|
2976
|
+
|
|
2955
2977
|
name = self._go2rtc_stream_name()
|
|
2956
2978
|
base = self._go2rtc_url
|
|
2957
2979
|
if not base:
|
|
@@ -2968,6 +2990,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2968
2990
|
"no go2rtc url and the serve is an RTSP push to a shared "
|
|
2969
2991
|
"port; cannot tell whether anyone is watching",
|
|
2970
2992
|
)
|
|
2993
|
+
self._viewer_cache = (_now_mono, None)
|
|
2971
2994
|
return None
|
|
2972
2995
|
if base and name:
|
|
2973
2996
|
try:
|
|
@@ -2977,10 +3000,14 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2977
3000
|
async with aiohttp.ClientSession() as _s:
|
|
2978
3001
|
viewers = await Go2rtcClient(_s, base).viewer_count(name)
|
|
2979
3002
|
if viewers is not None:
|
|
2980
|
-
|
|
3003
|
+
_answer = viewers > 0
|
|
3004
|
+
self._viewer_cache = (_now_mono, _answer)
|
|
3005
|
+
return _answer
|
|
2981
3006
|
except Exception as exc:
|
|
2982
3007
|
_LOGGER.debug("go2rtc viewer check failed: %s", exc)
|
|
2983
|
-
|
|
3008
|
+
_answer = self._sdes_serve_consumer_present(port)
|
|
3009
|
+
self._viewer_cache = (_now_mono, _answer)
|
|
3010
|
+
return _answer
|
|
2984
3011
|
|
|
2985
3012
|
async def _sdes_keepalive_loop(self) -> None:
|
|
2986
3013
|
"""Keep the cold-start serve relay alive around the SDES keepalive loop.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.12.
|
|
7
|
+
version = "0.12.12"
|
|
8
8
|
description = "Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12/python_aidot_cameras.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.12
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""The viewer check must not be asked of go2rtc on every watchdog tick.
|
|
2
|
+
|
|
3
|
+
The DTLS watchdog loops twice a second, and the go2rtc query opens a fresh HTTP
|
|
4
|
+
session per call - so an unthrottled check is two requests per second per camera,
|
|
5
|
+
forever, aimed at the very service that also has to serve the video. On a small
|
|
6
|
+
fleet that was enough to stop go2rtc answering at all, which presents as no video
|
|
7
|
+
anywhere. Idle release is measured in minutes, so a few seconds of staleness in
|
|
8
|
+
the answer costs nothing.
|
|
9
|
+
"""
|
|
10
|
+
import asyncio
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
import aidot_cameras.camera.client as cc
|
|
14
|
+
from aidot_cameras.device_client import CameraDeviceClient
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _client(answer):
|
|
18
|
+
c = CameraDeviceClient.__new__(CameraDeviceClient)
|
|
19
|
+
c._go2rtc_url = None
|
|
20
|
+
c._keepalive_rtsp_url = "http://127.0.0.1:18931/x.ts"
|
|
21
|
+
c.device_id = "abc123abc123def"
|
|
22
|
+
c._viewer_cache = (0.0, None)
|
|
23
|
+
calls = []
|
|
24
|
+
|
|
25
|
+
def _probe(port):
|
|
26
|
+
calls.append(port)
|
|
27
|
+
return answer
|
|
28
|
+
|
|
29
|
+
c._sdes_serve_consumer_present = _probe
|
|
30
|
+
return c, calls
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_repeated_calls_hit_the_backend_once():
|
|
34
|
+
c, calls = _client(True)
|
|
35
|
+
|
|
36
|
+
async def go():
|
|
37
|
+
for _ in range(40): # 40 watchdog ticks
|
|
38
|
+
await c._viewer_present(18931)
|
|
39
|
+
asyncio.run(go())
|
|
40
|
+
assert len(calls) == 1, f"backend hit {len(calls)} times, expected 1"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_the_cached_answer_is_returned_verbatim():
|
|
44
|
+
for answer in (True, False, None):
|
|
45
|
+
c, _ = _client(answer)
|
|
46
|
+
|
|
47
|
+
async def go():
|
|
48
|
+
first = await c._viewer_present(18931)
|
|
49
|
+
second = await c._viewer_present(18931)
|
|
50
|
+
return first, second
|
|
51
|
+
first, second = asyncio.run(go())
|
|
52
|
+
assert first == answer and second == answer
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_the_cache_expires_so_the_answer_can_change():
|
|
56
|
+
c, calls = _client(True)
|
|
57
|
+
|
|
58
|
+
async def go():
|
|
59
|
+
await c._viewer_present(18931)
|
|
60
|
+
# Pretend the interval has elapsed.
|
|
61
|
+
stamp, cached = c._viewer_cache
|
|
62
|
+
c._viewer_cache = (stamp - cc._VIEWER_CHECK_INTERVAL_S - 1, cached)
|
|
63
|
+
await c._viewer_present(18931)
|
|
64
|
+
asyncio.run(go())
|
|
65
|
+
assert len(calls) == 2
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_the_interval_is_long_enough_to_matter():
|
|
69
|
+
# The watchdog ticks every 0.5s; anything under a couple of seconds would
|
|
70
|
+
# still be a request storm.
|
|
71
|
+
assert cc._VIEWER_CHECK_INTERVAL_S >= 5.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/clock.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/stats.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/_vendor/aiortc/utils.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/models.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/camera/webrtc_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/aidot_cameras/device_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_serve_open_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_dtls_skip_signaling_wait.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_ice_config_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_idle_release_both_loops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_keyframe_prompter.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_light_active_color_mode.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_live_stream_param.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_credential_refetch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_mqtt_session_reconnect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_offline_keepalive_pause.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_open_fail_logger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_bridge_break_gate.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_echo_wait_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_fast_liveplay.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_ffmpeg_stderr_log.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_idle_release.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_open_cleanup.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_serve_audio.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_sdes_teardown_exit_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_security_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_serializable_login_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_stream_drain_ownership.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.12}/tests/test_token_file_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|