toposync-ext-streaming 0.4.8__tar.gz → 0.7.10__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.
- toposync_ext_streaming-0.4.8/README.md → toposync_ext_streaming-0.7.10/PKG-INFO +26 -0
- toposync_ext_streaming-0.4.8/PKG-INFO → toposync_ext_streaming-0.7.10/README.md +13 -13
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/pyproject.toml +1 -1
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/api/models.py +3 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/api/routes.py +90 -6
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/engine_manager.py +498 -32
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/.gitignore +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/LICENSE +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/LICENSE.ffmpeg +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/LICENSE.mediamtx +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/api/__init__.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/bin/ffmpeg/LICENSE +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/bin/mediamtx/LICENSE +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/extension.json +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/pipelines/__init__.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/pipelines/operators.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/plugin.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/326.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/326.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/387.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/4.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/4.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/623.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/623.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/703.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/703.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/main.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/main.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/static/remoteEntry.js +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/arbitration.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/camera_ingest.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/distributed_sync.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/encoder_state.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/ffmpeg_binary.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/go2rtc_binary.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/go2rtc_config.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/go2rtc_manager.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/ingest_auth.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/ingest_resolver.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/jsmpeg_manager.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/mediamtx_api_client.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/mediamtx_binary.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/mediamtx_config.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/mediamtx_processes.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/placeholder.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/platform.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/playback_events.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/publisher_manager.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/resize.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/runtime_state.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/streaming/writer_bridge.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/wizard/__init__.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/src/toposync_ext_streaming/wizard/pipeline_builder.py +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/package.json +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/activate.tsx +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/api/streamingApi.ts +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/constants.ts +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/entry.ts +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/settings/StreamingSettingsPanel.tsx +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/settings/SubModal.tsx +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/settings/WizardCreatePipelineFromTransmission.tsx +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/translations.ts +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/src/types.ts +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/tsconfig.json +0 -0
- {toposync_ext_streaming-0.4.8 → toposync_ext_streaming-0.7.10}/ui/webpack.config.js +0 -0
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toposync-ext-streaming
|
|
3
|
+
Version: 0.7.10
|
|
4
|
+
Summary: Toposync first-party extension: streaming settings, API surface, and pipeline sink bootstrap.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: numpy>=1.26
|
|
9
|
+
Requires-Dist: opencv-python-headless>=4.10
|
|
10
|
+
Requires-Dist: toposync-core>=0.3.3
|
|
11
|
+
Requires-Dist: websockets>=12.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
1
14
|
# Toposync Streaming Extension
|
|
2
15
|
|
|
3
16
|
Extension ID: `com.toposync.streaming`
|
|
@@ -729,6 +742,15 @@ All endpoints are under `/api/streams`.
|
|
|
729
742
|
### Home Assistant
|
|
730
743
|
- `GET /api/streams/home-assistant/cameras`
|
|
731
744
|
|
|
745
|
+
For Home Assistant running in Docker Compose, the RTSP URL in this manifest must
|
|
746
|
+
use a host reachable from the Home Assistant container. Enable
|
|
747
|
+
`engine.expose_to_lan`, restart the streaming engine, and set
|
|
748
|
+
`TOPOSYNC_HOME_ASSISTANT_RTSP_HOST` to the Compose service name or another
|
|
749
|
+
container-reachable hostname, for example `toposync`. If the endpoint would
|
|
750
|
+
otherwise return a loopback RTSP URL for a non-loopback request host, the
|
|
751
|
+
manifest omits `rtsp_url` and reports a blocking error instead of publishing an
|
|
752
|
+
unusable `rtsp://127.0.0.1:...` URL.
|
|
753
|
+
|
|
732
754
|
### Distributed settings
|
|
733
755
|
- `GET /api/streams/distributed/settings/{server_id}`
|
|
734
756
|
|
|
@@ -968,6 +990,10 @@ To enable LAN viewers:
|
|
|
968
990
|
|
|
969
991
|
Notes:
|
|
970
992
|
- `GET /api/streams/transmissions/{id}/urls` chooses the host component based on the current request host when `expose_to_lan` is enabled.
|
|
993
|
+
- `GET /api/streams/home-assistant/cameras` follows the same rule, but a
|
|
994
|
+
Compose-based Home Assistant often needs `TOPOSYNC_HOME_ASSISTANT_RTSP_HOST`
|
|
995
|
+
so the advertised RTSP host is the Toposync service name visible from the Home
|
|
996
|
+
Assistant container.
|
|
971
997
|
- If MediaMTX runs behind Docker/NAT, advertise the reachable LAN host with `TOPOSYNC_STREAMING_WEBRTC_ADDITIONAL_HOSTS` and publish the static ICE UDP port configured by `TOPOSYNC_STREAMING_WEBRTC_LOCAL_UDP_ADDRESS`.
|
|
972
998
|
- MediaMTX still restricts publishing and API access to localhost IPs; LAN exposure is for viewer playback only.
|
|
973
999
|
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: toposync-ext-streaming
|
|
3
|
-
Version: 0.4.8
|
|
4
|
-
Summary: Toposync first-party extension: streaming settings, API surface, and pipeline sink bootstrap.
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: numpy>=1.26
|
|
9
|
-
Requires-Dist: opencv-python-headless>=4.10
|
|
10
|
-
Requires-Dist: toposync-core>=0.3.3
|
|
11
|
-
Requires-Dist: websockets>=12.0
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
|
|
14
1
|
# Toposync Streaming Extension
|
|
15
2
|
|
|
16
3
|
Extension ID: `com.toposync.streaming`
|
|
@@ -742,6 +729,15 @@ All endpoints are under `/api/streams`.
|
|
|
742
729
|
### Home Assistant
|
|
743
730
|
- `GET /api/streams/home-assistant/cameras`
|
|
744
731
|
|
|
732
|
+
For Home Assistant running in Docker Compose, the RTSP URL in this manifest must
|
|
733
|
+
use a host reachable from the Home Assistant container. Enable
|
|
734
|
+
`engine.expose_to_lan`, restart the streaming engine, and set
|
|
735
|
+
`TOPOSYNC_HOME_ASSISTANT_RTSP_HOST` to the Compose service name or another
|
|
736
|
+
container-reachable hostname, for example `toposync`. If the endpoint would
|
|
737
|
+
otherwise return a loopback RTSP URL for a non-loopback request host, the
|
|
738
|
+
manifest omits `rtsp_url` and reports a blocking error instead of publishing an
|
|
739
|
+
unusable `rtsp://127.0.0.1:...` URL.
|
|
740
|
+
|
|
745
741
|
### Distributed settings
|
|
746
742
|
- `GET /api/streams/distributed/settings/{server_id}`
|
|
747
743
|
|
|
@@ -981,6 +977,10 @@ To enable LAN viewers:
|
|
|
981
977
|
|
|
982
978
|
Notes:
|
|
983
979
|
- `GET /api/streams/transmissions/{id}/urls` chooses the host component based on the current request host when `expose_to_lan` is enabled.
|
|
980
|
+
- `GET /api/streams/home-assistant/cameras` follows the same rule, but a
|
|
981
|
+
Compose-based Home Assistant often needs `TOPOSYNC_HOME_ASSISTANT_RTSP_HOST`
|
|
982
|
+
so the advertised RTSP host is the Toposync service name visible from the Home
|
|
983
|
+
Assistant container.
|
|
984
984
|
- If MediaMTX runs behind Docker/NAT, advertise the reachable LAN host with `TOPOSYNC_STREAMING_WEBRTC_ADDITIONAL_HOSTS` and publish the static ICE UDP port configured by `TOPOSYNC_STREAMING_WEBRTC_LOCAL_UDP_ADDRESS`.
|
|
985
985
|
- MediaMTX still restricts publishing and API access to localhost IPs; LAN exposure is for viewer playback only.
|
|
986
986
|
|
|
@@ -1034,6 +1034,9 @@ class StreamingEngineStatusResponse(BaseModel):
|
|
|
1034
1034
|
warnings: list[str] = Field(default_factory=list)
|
|
1035
1035
|
restart_count: int = Field(default=0, ge=0)
|
|
1036
1036
|
orphan_pids: list[int] = Field(default_factory=list)
|
|
1037
|
+
port_policy: Literal["stable", "flexible"] = "flexible"
|
|
1038
|
+
port_resolution: str = "preferred"
|
|
1039
|
+
port_blocking_errors: list[str] = Field(default_factory=list)
|
|
1037
1040
|
|
|
1038
1041
|
|
|
1039
1042
|
class StreamingMseSidecarStatusResponse(BaseModel):
|
|
@@ -33,7 +33,7 @@ from toposync.runtime.pipelines.operators_sinks import _encode_image_bytes
|
|
|
33
33
|
from toposync.runtime.pipelines.templates import camera_names_by_id, safe_pipeline_name
|
|
34
34
|
from toposync.runtime.services import ServiceRegistry
|
|
35
35
|
|
|
36
|
-
from ..streaming.engine_manager import MediaMtxEngineManager
|
|
36
|
+
from ..streaming.engine_manager import MediaMtxEngineManager, MediaMtxPortResolutionError
|
|
37
37
|
from ..streaming.go2rtc_binary import extract_go2rtc_binary, find_installed_go2rtc_binary
|
|
38
38
|
from ..streaming.go2rtc_manager import Go2RtcSidecarManager, Go2RtcSidecarStatus
|
|
39
39
|
from ..streaming.camera_ingest import (
|
|
@@ -852,7 +852,19 @@ def _build_network_contract(
|
|
|
852
852
|
media_url_origin=media_url_origin,
|
|
853
853
|
)
|
|
854
854
|
|
|
855
|
-
|
|
855
|
+
explicit_expected_ports = any(
|
|
856
|
+
value is not None
|
|
857
|
+
for value in (
|
|
858
|
+
expected_ports.direct_api,
|
|
859
|
+
expected_ports.rtsp,
|
|
860
|
+
expected_ports.hls,
|
|
861
|
+
expected_ports.webrtc,
|
|
862
|
+
expected_ports.webrtc_udp,
|
|
863
|
+
expected_ports.api,
|
|
864
|
+
)
|
|
865
|
+
)
|
|
866
|
+
strict_contract = environment == "home_assistant_addon" or explicit_expected_ports
|
|
867
|
+
fail_on_mismatch = _env_bool("TOPOSYNC_FAIL_STREAM_URLS_ON_PORT_MISMATCH") or strict_contract
|
|
856
868
|
status: Literal["ok", "port_mismatch", "proxy_required", "proxy_unavailable"] = "ok"
|
|
857
869
|
|
|
858
870
|
def compare_port(
|
|
@@ -869,11 +881,16 @@ def _build_network_contract(
|
|
|
869
881
|
actual = getattr(actual_ports, key)
|
|
870
882
|
if expected is None or actual is None or int(expected) == int(actual):
|
|
871
883
|
return
|
|
872
|
-
|
|
884
|
+
expected_label = (
|
|
885
|
+
"expected add-on port"
|
|
886
|
+
if environment == "home_assistant_addon"
|
|
887
|
+
else "expected streaming port"
|
|
888
|
+
)
|
|
889
|
+
message = f"{label} active port {actual} does not match {expected_label} {expected}."
|
|
873
890
|
warnings.append(message)
|
|
874
891
|
if status == "ok":
|
|
875
892
|
status = "port_mismatch"
|
|
876
|
-
if blocking_when_failed and fail_on_mismatch:
|
|
893
|
+
if (blocking_when_failed and fail_on_mismatch) or strict_contract:
|
|
877
894
|
blocking_errors.append(message)
|
|
878
895
|
|
|
879
896
|
compare_port(
|
|
@@ -2626,7 +2643,12 @@ def _url_has_loopback_host(url: str | None) -> bool:
|
|
|
2626
2643
|
host = str(urllib_parse.urlsplit(str(url or "")).hostname or "").strip().lower()
|
|
2627
2644
|
except Exception:
|
|
2628
2645
|
return False
|
|
2629
|
-
return host
|
|
2646
|
+
return _host_is_loopback(host)
|
|
2647
|
+
|
|
2648
|
+
|
|
2649
|
+
def _host_is_loopback(host: str | None) -> bool:
|
|
2650
|
+
normalized = str(host or "").strip().lower()
|
|
2651
|
+
return normalized in {"localhost", "::1"} or normalized.startswith("127.")
|
|
2630
2652
|
|
|
2631
2653
|
|
|
2632
2654
|
def _url_host_for_rtsp(url: str | None) -> str:
|
|
@@ -2940,10 +2962,17 @@ async def _build_home_assistant_camera_item(
|
|
|
2940
2962
|
blocking_errors.append(f"Failed to resolve remote Home Assistant camera playback: {exc}")
|
|
2941
2963
|
else:
|
|
2942
2964
|
engine_path = resolve_output_engine_path(transmission, output)
|
|
2965
|
+
home_assistant_rtsp_host = _home_assistant_rtsp_host(request)
|
|
2943
2966
|
rtsp_url = await _engine_manager(request).get_read_url_for_path(
|
|
2944
2967
|
engine_path,
|
|
2945
|
-
host=
|
|
2968
|
+
host=home_assistant_rtsp_host,
|
|
2946
2969
|
)
|
|
2970
|
+
if _url_has_loopback_host(rtsp_url) and not _host_is_loopback(home_assistant_rtsp_host):
|
|
2971
|
+
blocking_errors.append(
|
|
2972
|
+
"Local streaming engine returned a loopback RTSP URL for Home Assistant camera playback; "
|
|
2973
|
+
"enable engine.expose_to_lan and set TOPOSYNC_HOME_ASSISTANT_RTSP_HOST to a host reachable by Home Assistant."
|
|
2974
|
+
)
|
|
2975
|
+
rtsp_url = None
|
|
2947
2976
|
redacted_rtsp_url = _redact_url_credentials(rtsp_url)
|
|
2948
2977
|
|
|
2949
2978
|
role = variant.role if variant is not None else None
|
|
@@ -6153,6 +6182,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
6153
6182
|
path_auth=path_auth,
|
|
6154
6183
|
path_configs=path_configs,
|
|
6155
6184
|
)
|
|
6185
|
+
except MediaMtxPortResolutionError as exc:
|
|
6186
|
+
raise HTTPException(
|
|
6187
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
6188
|
+
) from exc
|
|
6156
6189
|
except Exception as exc:
|
|
6157
6190
|
raise HTTPException(
|
|
6158
6191
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -6277,6 +6310,7 @@ def create_streaming_router() -> APIRouter:
|
|
|
6277
6310
|
|
|
6278
6311
|
warnings: list[str] = []
|
|
6279
6312
|
warnings.extend(list(status.warnings))
|
|
6313
|
+
warnings.extend(list(status.port_blocking_errors))
|
|
6280
6314
|
if not settings.engine.enabled:
|
|
6281
6315
|
warnings.append("Engine is disabled in settings.")
|
|
6282
6316
|
if settings.engine.enabled and not status.running:
|
|
@@ -6354,6 +6388,9 @@ def create_streaming_router() -> APIRouter:
|
|
|
6354
6388
|
warnings=warnings,
|
|
6355
6389
|
restart_count=status.restart_count,
|
|
6356
6390
|
orphan_pids=orphan_pids,
|
|
6391
|
+
port_policy=status.port_policy if status.port_policy in {"stable", "flexible"} else "flexible",
|
|
6392
|
+
port_resolution=status.port_resolution,
|
|
6393
|
+
port_blocking_errors=list(status.port_blocking_errors),
|
|
6357
6394
|
)
|
|
6358
6395
|
|
|
6359
6396
|
@router.post("/engine/download", response_model=StreamingEngineStatusResponse)
|
|
@@ -6416,6 +6453,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
6416
6453
|
),
|
|
6417
6454
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
6418
6455
|
)
|
|
6456
|
+
except MediaMtxPortResolutionError as exc:
|
|
6457
|
+
raise HTTPException(
|
|
6458
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
6459
|
+
) from exc
|
|
6419
6460
|
except Exception as exc:
|
|
6420
6461
|
raise HTTPException(
|
|
6421
6462
|
status_code=500, detail=f"Failed to start streaming engine: {exc}"
|
|
@@ -6477,6 +6518,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
6477
6518
|
),
|
|
6478
6519
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
6479
6520
|
)
|
|
6521
|
+
except MediaMtxPortResolutionError as exc:
|
|
6522
|
+
raise HTTPException(
|
|
6523
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
6524
|
+
) from exc
|
|
6480
6525
|
except Exception as exc:
|
|
6481
6526
|
raise HTTPException(
|
|
6482
6527
|
status_code=500, detail=f"Failed to restart streaming engine: {exc}"
|
|
@@ -6532,6 +6577,12 @@ def create_streaming_router() -> APIRouter:
|
|
|
6532
6577
|
),
|
|
6533
6578
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
6534
6579
|
)
|
|
6580
|
+
except MediaMtxPortResolutionError as exc:
|
|
6581
|
+
suffix = f" (killed {len(killed_pids)} stale process(es))" if killed_pids else ""
|
|
6582
|
+
raise HTTPException(
|
|
6583
|
+
status_code=409,
|
|
6584
|
+
detail=f"Streaming engine port contract is blocked: {exc}{suffix}",
|
|
6585
|
+
) from exc
|
|
6535
6586
|
except Exception as exc:
|
|
6536
6587
|
suffix = f" (killed {len(killed_pids)} stale process(es))" if killed_pids else ""
|
|
6537
6588
|
raise HTTPException(
|
|
@@ -6864,6 +6915,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
6864
6915
|
await _apply_streaming_engine_state(request, settings=saved)
|
|
6865
6916
|
except HTTPException:
|
|
6866
6917
|
raise
|
|
6918
|
+
except MediaMtxPortResolutionError as exc:
|
|
6919
|
+
raise HTTPException(
|
|
6920
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
6921
|
+
) from exc
|
|
6867
6922
|
except Exception as exc:
|
|
6868
6923
|
raise HTTPException(
|
|
6869
6924
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -6911,6 +6966,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
6911
6966
|
await _apply_streaming_engine_state(request, settings=saved)
|
|
6912
6967
|
except HTTPException:
|
|
6913
6968
|
raise
|
|
6969
|
+
except MediaMtxPortResolutionError as exc:
|
|
6970
|
+
raise HTTPException(
|
|
6971
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
6972
|
+
) from exc
|
|
6914
6973
|
except Exception as exc:
|
|
6915
6974
|
raise HTTPException(
|
|
6916
6975
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -7111,6 +7170,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
7111
7170
|
),
|
|
7112
7171
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
7113
7172
|
)
|
|
7173
|
+
except MediaMtxPortResolutionError as exc:
|
|
7174
|
+
raise HTTPException(
|
|
7175
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
7176
|
+
) from exc
|
|
7114
7177
|
except Exception as exc:
|
|
7115
7178
|
raise HTTPException(
|
|
7116
7179
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -7202,6 +7265,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
7202
7265
|
),
|
|
7203
7266
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
7204
7267
|
)
|
|
7268
|
+
except MediaMtxPortResolutionError as exc:
|
|
7269
|
+
raise HTTPException(
|
|
7270
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
7271
|
+
) from exc
|
|
7205
7272
|
except Exception as exc:
|
|
7206
7273
|
raise HTTPException(
|
|
7207
7274
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -7271,6 +7338,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
7271
7338
|
),
|
|
7272
7339
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
7273
7340
|
)
|
|
7341
|
+
except MediaMtxPortResolutionError as exc:
|
|
7342
|
+
raise HTTPException(
|
|
7343
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
7344
|
+
) from exc
|
|
7274
7345
|
except Exception as exc:
|
|
7275
7346
|
raise HTTPException(
|
|
7276
7347
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -7342,6 +7413,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
7342
7413
|
),
|
|
7343
7414
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
7344
7415
|
)
|
|
7416
|
+
except MediaMtxPortResolutionError as exc:
|
|
7417
|
+
raise HTTPException(
|
|
7418
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
7419
|
+
) from exc
|
|
7345
7420
|
except Exception as exc:
|
|
7346
7421
|
raise HTTPException(
|
|
7347
7422
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -7390,6 +7465,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
7390
7465
|
),
|
|
7391
7466
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
7392
7467
|
)
|
|
7468
|
+
except MediaMtxPortResolutionError as exc:
|
|
7469
|
+
raise HTTPException(
|
|
7470
|
+
status_code=409, detail=f"Streaming engine port contract is blocked: {exc}"
|
|
7471
|
+
) from exc
|
|
7393
7472
|
except Exception as exc:
|
|
7394
7473
|
raise HTTPException(
|
|
7395
7474
|
status_code=500, detail=f"Failed to apply streaming settings: {exc}"
|
|
@@ -8646,6 +8725,11 @@ def create_streaming_router() -> APIRouter:
|
|
|
8646
8725
|
),
|
|
8647
8726
|
path_configs=build_camera_ingest_path_configs(camera_ingest_by_id),
|
|
8648
8727
|
)
|
|
8728
|
+
except MediaMtxPortResolutionError as exc:
|
|
8729
|
+
raise HTTPException(
|
|
8730
|
+
status_code=409,
|
|
8731
|
+
detail=f"Streaming engine port contract is blocked: {exc}",
|
|
8732
|
+
) from exc
|
|
8649
8733
|
except Exception as exc:
|
|
8650
8734
|
raise HTTPException(
|
|
8651
8735
|
status_code=500, detail=f"Failed to rotate ingest credentials: {exc}"
|