python-aidot-cameras 0.13.0__tar.gz → 0.13.1__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.13.0/python_aidot_cameras.egg-info → python_aidot_cameras-0.13.1}/PKG-INFO +1 -1
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/client.py +70 -18
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/pyproject.toml +1 -1
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
- python_aidot_cameras-0.13.1/tests/test_viewer_present_push_mode.py +236 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/LICENSE +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/README.md +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/sdes_open.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/setup.cfg +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_ice_peer_reflexive.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_live_validate_policy.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_wake_readiness_retry.py +0 -0
{python_aidot_cameras-0.13.0/python_aidot_cameras.egg-info → python_aidot_cameras-0.13.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
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
|
|
@@ -854,6 +854,10 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
854
854
|
self._last_frame_time: float = 0.0
|
|
855
855
|
self._keepalive_rtsp_url: Optional[str] = None # local serve URL (go2rtc pulls)
|
|
856
856
|
self._go2rtc_url: Optional[str] = None # go2rtc API base (prefer-go2rtc)
|
|
857
|
+
# Whether WE own this camera's go2rtc stream registration. False when the
|
|
858
|
+
# consumer registers the stream itself and passes the URL for viewer
|
|
859
|
+
# queries only - in which case we must neither add nor REMOVE it.
|
|
860
|
+
self._go2rtc_manages_stream: bool = True
|
|
857
861
|
self._go2rtc_pull_url: Optional[str] = None # go2rtc pull URL once registered
|
|
858
862
|
self._go2rtc_task: "Optional[asyncio.Task[None]]" = None
|
|
859
863
|
# Set by the DTLS serve loop once ffmpeg is bound + serving, so
|
|
@@ -2789,6 +2793,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2789
2793
|
fast_connect: Optional[bool] = None,
|
|
2790
2794
|
sdes_audio: Optional[bool] = None,
|
|
2791
2795
|
go2rtc_url: Optional[str] = None,
|
|
2796
|
+
go2rtc_register: bool = True,
|
|
2792
2797
|
live_stream_param: Optional[bool] = None,
|
|
2793
2798
|
serve_relay: Optional[bool] = None,
|
|
2794
2799
|
stream_idle_s: Optional[float] = None,
|
|
@@ -2828,6 +2833,22 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2828
2833
|
``AIDOT_SERVE_RELAY`` env var (default on); set False to serve ffmpeg
|
|
2829
2834
|
directly on the public port (the pre-0.7.18 behaviour).
|
|
2830
2835
|
|
|
2836
|
+
``go2rtc_url`` points at a go2rtc API base. It does two things: register
|
|
2837
|
+
the local serve with go2rtc (exposing its low-latency pull URL), and let
|
|
2838
|
+
the idle-release logic ask go2rtc **who is watching**.
|
|
2839
|
+
|
|
2840
|
+
``go2rtc_register=False`` keeps the second and drops the first, for a
|
|
2841
|
+
consumer that registers the stream itself - registering it twice
|
|
2842
|
+
re-points the source mid-stream and was observed leaving DTLS cameras
|
|
2843
|
+
producing nothing at all. Such a consumer must still pass ``go2rtc_url``:
|
|
2844
|
+
withholding it does not merely lose a nicety, it silently disables viewer
|
|
2845
|
+
detection, and in SDES **push** mode that is the only viewer signal there
|
|
2846
|
+
is (the serve port is go2rtc's shared RTSP port, where every camera's own
|
|
2847
|
+
publisher is connected, so a socket check would report a viewer for every
|
|
2848
|
+
camera forever). With no signal ``_viewer_present`` answers "unknown",
|
|
2849
|
+
``_idle_release_due`` correctly never releases on unknown, and a battery
|
|
2850
|
+
camera's keepalive renews forever against a camera nobody is watching.
|
|
2851
|
+
|
|
2831
2852
|
``stream_idle_s`` sets the no-viewer idle-release window in seconds,
|
|
2832
2853
|
overriding the ``AIDOT_STREAM_IDLE_S`` env default (120). ``0`` (or
|
|
2833
2854
|
negative) keeps the warm WebRTC session forever so re-views are instant
|
|
@@ -2860,6 +2881,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2860
2881
|
return
|
|
2861
2882
|
self._keepalive_rtsp_url = rtsp_push_url
|
|
2862
2883
|
self._go2rtc_url = go2rtc_url
|
|
2884
|
+
self._go2rtc_manages_stream = bool(go2rtc_register)
|
|
2863
2885
|
self._streaming_active = True
|
|
2864
2886
|
# App parity: keep a battery camera awake for the whole view (mains cams
|
|
2865
2887
|
# never sleep, so this is a no-op there). Single-instance; cancelled in
|
|
@@ -2874,7 +2896,18 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2874
2896
|
# Prefer-go2rtc: if a go2rtc server is configured, register the local
|
|
2875
2897
|
# serve with it once ready and expose its pull URL (ffmpeg fallback
|
|
2876
2898
|
# otherwise). Opt-in: with no go2rtc_url, behaviour is unchanged.
|
|
2877
|
-
|
|
2899
|
+
#
|
|
2900
|
+
# ``go2rtc_register=False`` stores the URL for QUERIES ONLY and skips
|
|
2901
|
+
# registration. That exists for a consumer that registers the stream
|
|
2902
|
+
# itself: registering it twice re-points the source while a stream is
|
|
2903
|
+
# mid-flight, which was observed leaving DTLS cameras producing nothing
|
|
2904
|
+
# at all. Such a consumer previously had to withhold go2rtc_url
|
|
2905
|
+
# entirely, which silently disabled viewer detection - and in SDES push
|
|
2906
|
+
# mode that means a battery camera can never idle-release, because
|
|
2907
|
+
# ``_viewer_present`` answers "unknown" and ``_idle_release_due``
|
|
2908
|
+
# (correctly) never releases on unknown. The keepalive then renews
|
|
2909
|
+
# forever against a camera nobody is watching.
|
|
2910
|
+
if go2rtc_url and go2rtc_register:
|
|
2878
2911
|
self._go2rtc_task = asyncio.ensure_future(self._register_with_go2rtc())
|
|
2879
2912
|
|
|
2880
2913
|
@property
|
|
@@ -2934,9 +2967,17 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2934
2967
|
_LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_register_with_go2rtc', exc_info=True)
|
|
2935
2968
|
|
|
2936
2969
|
async def _deregister_go2rtc(self) -> None:
|
|
2937
|
-
"""Remove this camera's stream from go2rtc (best-effort).
|
|
2970
|
+
"""Remove this camera's stream from go2rtc (best-effort).
|
|
2971
|
+
|
|
2972
|
+
Only ever removes a registration this client created. With
|
|
2973
|
+
``go2rtc_register=False`` the consumer owns the stream and merely lent us
|
|
2974
|
+
the URL to ask who is watching; deleting its stream on stop or
|
|
2975
|
+
idle-release would tear down the very thing it is serving from.
|
|
2976
|
+
"""
|
|
2938
2977
|
base = self._go2rtc_url
|
|
2939
2978
|
self._go2rtc_pull_url = None
|
|
2979
|
+
if not self._go2rtc_manages_stream:
|
|
2980
|
+
return
|
|
2940
2981
|
# Deliberately NOT gated on `pull`: go2rtc answers the register call with
|
|
2941
2982
|
# 400 when it cannot immediately validate the source (a camera that has
|
|
2942
2983
|
# not started producing yet), yet it still keeps the stream registered.
|
|
@@ -3099,22 +3140,21 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3099
3140
|
|
|
3100
3141
|
name = self._go2rtc_stream_name()
|
|
3101
3142
|
base = self._go2rtc_url
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
return None
|
|
3143
|
+
|
|
3144
|
+
# In PUSH mode the "serve port" is go2rtc's SHARED RTSP port (8554),
|
|
3145
|
+
# where every camera's own publisher is connected - so the socket
|
|
3146
|
+
# fallback below is not merely unavailable, it is WRONG: it would report
|
|
3147
|
+
# a viewer for every camera forever and nothing would ever release.
|
|
3148
|
+
# Tracked separately from `base` because it must gate the fallback even
|
|
3149
|
+
# when go2rtc IS configured but the query fails.
|
|
3150
|
+
push_mode = bool(self._keepalive_rtsp_url) and not str(
|
|
3151
|
+
self._keepalive_rtsp_url).startswith("http")
|
|
3152
|
+
|
|
3153
|
+
# Ask go2rtc when we can. This is the only viewer signal that works in
|
|
3154
|
+
# push mode, which is why a consumer that registers its own stream
|
|
3155
|
+
# should still pass go2rtc_url with go2rtc_register=False rather than
|
|
3156
|
+
# withholding it (withholding it silently pins push-mode cameras to
|
|
3157
|
+
# "unknown", and unknown never releases).
|
|
3118
3158
|
if base and name:
|
|
3119
3159
|
try:
|
|
3120
3160
|
import aiohttp
|
|
@@ -3128,6 +3168,18 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3128
3168
|
return _answer
|
|
3129
3169
|
except Exception as exc:
|
|
3130
3170
|
_LOGGER.debug("go2rtc viewer check failed: %s", exc)
|
|
3171
|
+
|
|
3172
|
+
if push_mode:
|
|
3173
|
+
# Answer "unknown" instead of a confident lie - the caller then holds
|
|
3174
|
+
# the stream rather than tearing down a live view.
|
|
3175
|
+
_LOGGER.debug(
|
|
3176
|
+
"cannot tell whether anyone is watching: the serve is an RTSP "
|
|
3177
|
+
"push to a shared port and go2rtc %s",
|
|
3178
|
+
"did not answer" if base else "was not configured",
|
|
3179
|
+
)
|
|
3180
|
+
self._viewer_cache = (_now_mono, None)
|
|
3181
|
+
return None
|
|
3182
|
+
|
|
3131
3183
|
_answer = self._sdes_serve_consumer_present(port)
|
|
3132
3184
|
self._viewer_cache = (_now_mono, _answer)
|
|
3133
3185
|
return _answer
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.13.
|
|
7
|
+
version = "0.13.1"
|
|
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.13.0 → python_aidot_cameras-0.13.1/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.13.
|
|
3
|
+
Version: 0.13.1
|
|
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
|
|
@@ -151,5 +151,6 @@ tests/test_turn_create_permission.py
|
|
|
151
151
|
tests/test_upstream_compat.py
|
|
152
152
|
tests/test_viewer_check_throttle.py
|
|
153
153
|
tests/test_viewer_presence.py
|
|
154
|
+
tests/test_viewer_present_push_mode.py
|
|
154
155
|
tests/test_wait_or_event.py
|
|
155
156
|
tests/test_wake_readiness_retry.py
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""``_viewer_present`` in SDES push mode, and the ``go2rtc_register`` seam.
|
|
2
|
+
|
|
3
|
+
Why this file exists, in one paragraph:
|
|
4
|
+
|
|
5
|
+
In push mode the "serve port" is go2rtc's SHARED RTSP port (8554), where every
|
|
6
|
+
camera's own publisher is connected - so a socket check there reports a viewer
|
|
7
|
+
for every camera forever. ``_viewer_present`` therefore refuses to use it and
|
|
8
|
+
answers "unknown", and ``_idle_release_due`` correctly never releases on
|
|
9
|
+
unknown. That is all deliberate and right. The trap is what it composes into: a
|
|
10
|
+
consumer that withholds ``go2rtc_url`` (to avoid a duplicate registration that
|
|
11
|
+
re-points the stream mid-flight) removes the ONLY viewer signal push mode has,
|
|
12
|
+
so every push-mode camera is pinned to "unknown" and a battery camera's
|
|
13
|
+
keepalive renews forever against a camera nobody is watching. Observed live: a
|
|
14
|
+
sleeping battery L2 still being renewed every ~100 s.
|
|
15
|
+
|
|
16
|
+
``go2rtc_register=False`` is the way out - keep the query, drop the
|
|
17
|
+
registration. These tests pin the decision table so neither half can regress
|
|
18
|
+
silently, since both failure modes are quiet ones.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import types
|
|
23
|
+
|
|
24
|
+
import pytest
|
|
25
|
+
|
|
26
|
+
from aidot_cameras.camera.protocol import _idle_release_due
|
|
27
|
+
|
|
28
|
+
PUSH_URL = "rtsp://127.0.0.1:8554/aidot_cam1" # go2rtc's shared RTSP port
|
|
29
|
+
HTTP_SERVE = "http://127.0.0.1:18000/aidot_cam1" # our own -listen socket
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _client(go2rtc_url=None, keepalive_url=PUSH_URL, viewers=None, raises=False):
|
|
33
|
+
"""A stand-in exposing exactly what ``_viewer_present`` touches.
|
|
34
|
+
|
|
35
|
+
Built by binding the real method onto a namespace rather than constructing a
|
|
36
|
+
CameraDeviceClient, which would need a device record, an account and a live
|
|
37
|
+
loop - none of which this decision depends on.
|
|
38
|
+
"""
|
|
39
|
+
from aidot_cameras.camera.client import CameraMixin
|
|
40
|
+
|
|
41
|
+
obj = types.SimpleNamespace(
|
|
42
|
+
# Sentinel, not None: if the throttle ever short-circuits, the test
|
|
43
|
+
# returns "STALE" and fails loudly instead of passing vacuously
|
|
44
|
+
# because the cached value happened to match the expected answer.
|
|
45
|
+
_viewer_cache=(0.0, "STALE"),
|
|
46
|
+
_go2rtc_url=go2rtc_url,
|
|
47
|
+
_keepalive_rtsp_url=keepalive_url,
|
|
48
|
+
_go2rtc_stream_name=lambda: "aidot_cam1",
|
|
49
|
+
_sdes_serve_consumer_present=lambda port: True, # the WRONG answer in push mode
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async def _fake_count(self, name):
|
|
53
|
+
if raises:
|
|
54
|
+
raise RuntimeError("go2rtc unreachable")
|
|
55
|
+
return viewers
|
|
56
|
+
|
|
57
|
+
obj._viewer_present = CameraMixin._viewer_present.__get__(obj)
|
|
58
|
+
obj._fake_count = _fake_count
|
|
59
|
+
return obj
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _ask(obj, monkeypatch, port=8554):
|
|
63
|
+
"""Call _viewer_present with the go2rtc client stubbed out."""
|
|
64
|
+
from aidot_cameras.camera import client as client_mod
|
|
65
|
+
|
|
66
|
+
class _Stub:
|
|
67
|
+
def __init__(self, session, base):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
async def viewer_count(self, name):
|
|
71
|
+
return await obj._fake_count(obj, name)
|
|
72
|
+
|
|
73
|
+
class _Sess:
|
|
74
|
+
async def __aenter__(self):
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
async def __aexit__(self, *a):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
import aidot_cameras.camera.go2rtc as g2
|
|
81
|
+
monkeypatch.setattr(g2, "Go2rtcClient", _Stub)
|
|
82
|
+
monkeypatch.setattr(client_mod, "_VIEWER_CHECK_INTERVAL_S", 0, raising=False)
|
|
83
|
+
|
|
84
|
+
import aiohttp
|
|
85
|
+
monkeypatch.setattr(aiohttp, "ClientSession", lambda *a, **k: _Sess())
|
|
86
|
+
return asyncio.run(obj._viewer_present(port))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --------------------------------------------------------------------------- #
|
|
90
|
+
# push mode: the socket fallback must never be consulted
|
|
91
|
+
# --------------------------------------------------------------------------- #
|
|
92
|
+
|
|
93
|
+
def test_push_mode_without_go2rtc_is_unknown_not_a_confident_lie(monkeypatch):
|
|
94
|
+
"""No go2rtc + push mode -> unknown.
|
|
95
|
+
|
|
96
|
+
The stub's socket check returns True; if it were ever consulted the answer
|
|
97
|
+
would be True, every camera would look watched, and nothing would release.
|
|
98
|
+
"""
|
|
99
|
+
obj = _client(go2rtc_url=None)
|
|
100
|
+
assert _ask(obj, monkeypatch) is None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_push_mode_falls_back_to_unknown_when_go2rtc_query_fails(monkeypatch):
|
|
104
|
+
"""go2rtc configured but unreachable must NOT drop to the socket check.
|
|
105
|
+
|
|
106
|
+
This is the case the restructure fixed: previously the shared-port guard sat
|
|
107
|
+
behind `if not base`, so configuring go2rtc and having the query fail fell
|
|
108
|
+
straight through to the wrong check.
|
|
109
|
+
"""
|
|
110
|
+
obj = _client(go2rtc_url="http://go2rtc:1984", raises=True)
|
|
111
|
+
assert _ask(obj, monkeypatch) is None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_push_mode_falls_back_to_unknown_when_go2rtc_returns_none(monkeypatch):
|
|
115
|
+
obj = _client(go2rtc_url="http://go2rtc:1984", viewers=None)
|
|
116
|
+
assert _ask(obj, monkeypatch) is None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# --------------------------------------------------------------------------- #
|
|
120
|
+
# push mode WITH go2rtc: real answers, which is the whole point
|
|
121
|
+
# --------------------------------------------------------------------------- #
|
|
122
|
+
|
|
123
|
+
def test_push_mode_with_go2rtc_reports_a_real_viewer(monkeypatch):
|
|
124
|
+
obj = _client(go2rtc_url="http://go2rtc:1984", viewers=2)
|
|
125
|
+
assert _ask(obj, monkeypatch) is True
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_push_mode_with_go2rtc_reports_nobody_watching(monkeypatch):
|
|
129
|
+
"""The answer that actually lets a battery camera release.
|
|
130
|
+
|
|
131
|
+
Without go2rtc this is unreachable in push mode - it answers unknown, and
|
|
132
|
+
unknown never releases.
|
|
133
|
+
"""
|
|
134
|
+
obj = _client(go2rtc_url="http://go2rtc:1984", viewers=0)
|
|
135
|
+
assert _ask(obj, monkeypatch) is False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# --------------------------------------------------------------------------- #
|
|
139
|
+
# non-push (our own HTTP -listen serve): the socket check is legitimate there
|
|
140
|
+
# --------------------------------------------------------------------------- #
|
|
141
|
+
|
|
142
|
+
def test_http_serve_still_uses_the_socket_check(monkeypatch):
|
|
143
|
+
"""On our own serve port the socket check is meaningful and must be kept."""
|
|
144
|
+
obj = _client(go2rtc_url=None, keepalive_url=HTTP_SERVE)
|
|
145
|
+
assert _ask(obj, monkeypatch) is True
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# --------------------------------------------------------------------------- #
|
|
149
|
+
# the composition that caused the live bug
|
|
150
|
+
# --------------------------------------------------------------------------- #
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize("answer,expected_release", [
|
|
153
|
+
(True, False), # someone watching -> hold
|
|
154
|
+
(None, False), # unknown -> hold (fail-safe)
|
|
155
|
+
(False, True), # nobody watching -> release
|
|
156
|
+
])
|
|
157
|
+
def test_only_a_definite_no_releases(answer, expected_release):
|
|
158
|
+
"""`unknown` must never release - and therefore must never be the only answer.
|
|
159
|
+
|
|
160
|
+
Pinned here next to the push-mode tests because the live bug was not in
|
|
161
|
+
either half: `_viewer_present` answering unknown is right, and
|
|
162
|
+
`_idle_release_due` holding on unknown is right. The defect was that push
|
|
163
|
+
mode without go2rtc made unknown the *only reachable* answer, so "never
|
|
164
|
+
release" became unconditional.
|
|
165
|
+
"""
|
|
166
|
+
assert _idle_release_due(answer, last_consumer=0.0, now=10_000.0,
|
|
167
|
+
idle_secs=120.0) is expected_release
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# --------------------------------------------------------------------------- #
|
|
171
|
+
# the registration seam
|
|
172
|
+
# --------------------------------------------------------------------------- #
|
|
173
|
+
|
|
174
|
+
def test_deregister_never_removes_a_stream_we_did_not_register():
|
|
175
|
+
"""The dangerous half of the seam.
|
|
176
|
+
|
|
177
|
+
``_deregister_go2rtc`` gates on ``_go2rtc_url`` alone. Before this seam
|
|
178
|
+
existed a query-only consumer withheld that URL, so deregistration was
|
|
179
|
+
unreachable by accident. Passing the URL for queries makes it reachable - and
|
|
180
|
+
it would then DELETE the stream the consumer registered itself, on every stop
|
|
181
|
+
and every idle-release, tearing down the thing it serves from.
|
|
182
|
+
"""
|
|
183
|
+
from aidot_cameras.camera.client import CameraMixin
|
|
184
|
+
|
|
185
|
+
removed = []
|
|
186
|
+
|
|
187
|
+
class _Client:
|
|
188
|
+
def __init__(self, session, base):
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
async def remove_stream(self, name):
|
|
192
|
+
removed.append(name)
|
|
193
|
+
|
|
194
|
+
class _Sess:
|
|
195
|
+
async def __aenter__(self):
|
|
196
|
+
return self
|
|
197
|
+
|
|
198
|
+
async def __aexit__(self, *a):
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
import aidot_cameras.camera.go2rtc as g2
|
|
202
|
+
import aiohttp
|
|
203
|
+
_orig_client, _orig_sess = g2.Go2rtcClient, aiohttp.ClientSession
|
|
204
|
+
g2.Go2rtcClient = _Client
|
|
205
|
+
aiohttp.ClientSession = lambda *a, **k: _Sess()
|
|
206
|
+
try:
|
|
207
|
+
for manages, expected in ((False, []), (True, ["aidot_cam1"])):
|
|
208
|
+
removed.clear()
|
|
209
|
+
obj = types.SimpleNamespace(
|
|
210
|
+
_go2rtc_url="http://go2rtc:1984",
|
|
211
|
+
_go2rtc_pull_url="x",
|
|
212
|
+
_go2rtc_manages_stream=manages,
|
|
213
|
+
_go2rtc_stream_name=lambda: "aidot_cam1",
|
|
214
|
+
device_id="cam1",
|
|
215
|
+
)
|
|
216
|
+
obj._deregister_go2rtc = CameraMixin._deregister_go2rtc.__get__(obj)
|
|
217
|
+
asyncio.run(obj._deregister_go2rtc())
|
|
218
|
+
assert removed == expected, (
|
|
219
|
+
f"manages={manages}: expected {expected}, got {removed}"
|
|
220
|
+
)
|
|
221
|
+
# The pull URL is cleared either way - it is ours regardless.
|
|
222
|
+
assert obj._go2rtc_pull_url is None
|
|
223
|
+
finally:
|
|
224
|
+
g2.Go2rtcClient, aiohttp.ClientSession = _orig_client, _orig_sess
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_start_keepalive_exposes_go2rtc_register():
|
|
228
|
+
import inspect
|
|
229
|
+
|
|
230
|
+
from aidot_cameras.camera.client import CameraMixin
|
|
231
|
+
|
|
232
|
+
params = inspect.signature(CameraMixin.start_keepalive).parameters
|
|
233
|
+
assert "go2rtc_register" in params
|
|
234
|
+
assert params["go2rtc_register"].default is True, (
|
|
235
|
+
"default must stay True so existing callers are unaffected"
|
|
236
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/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.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/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.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/stats.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/_vendor/aiortc/utils.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/sdes_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/aidot_cameras/camera/webrtc_open.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
|
|
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.13.0 → python_aidot_cameras-0.13.1}/tests/test_constants_imports_resolve.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dormant_camera_cleanup.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_mux_terminal_write.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_dtls_serve_open_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/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.13.0 → python_aidot_cameras-0.13.1}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_ice_peer_reflexive.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_idle_release_both_loops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_light_active_color_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_live_validate_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_credential_refetch.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_credential_self_heal.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_mqtt_session_reconnect.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_no_self_referential_go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_offline_keepalive_pause.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_persistent_mqtt_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_bridge_break_gate.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_echo_wait_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_fast_liveplay.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_ffmpeg_stderr_log.py
RENAMED
|
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.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdes_teardown_exit_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_sdp_payload_type_narrowing.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_security_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_serializable_login_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/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.13.0 → python_aidot_cameras-0.13.1}/tests/test_token_file_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_turn_create_permission.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_viewer_check_throttle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.13.0 → python_aidot_cameras-0.13.1}/tests/test_wake_readiness_retry.py
RENAMED
|
File without changes
|