toposync-ext-streaming 0.1.2__tar.gz → 0.1.4__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.1.2/README.md → toposync_ext_streaming-0.1.4/PKG-INFO +19 -0
- toposync_ext_streaming-0.1.2/PKG-INFO → toposync_ext_streaming-0.1.4/README.md +7 -12
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/pyproject.toml +1 -1
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/routes.py +4 -1
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/extension.json +1 -1
- toposync_ext_streaming-0.1.4/src/toposync_ext_streaming/static/703.js +2 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/engine_manager.py +31 -1
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_config.py +8 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/wizard/pipeline_builder.py +95 -3
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/StreamingSettingsPanel.tsx +4 -4
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/WizardCreatePipelineFromTransmission.tsx +8 -8
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/translations.ts +14 -14
- toposync_ext_streaming-0.1.2/src/toposync_ext_streaming/static/703.js +0 -2
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/.gitignore +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE.ffmpeg +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE.mediamtx +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/__init__.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/models.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/bin/ffmpeg/LICENSE +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/bin/mediamtx/LICENSE +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/pipelines/__init__.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/pipelines/operators.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/plugin.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/326.js +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/326.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/4.js +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/4.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/623.js +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/623.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/703.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/main.js +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/main.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/remoteEntry.js +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/arbitration.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/camera_ingest.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/distributed_sync.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/ffmpeg_binary.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_api_client.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_binary.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_processes.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/placeholder.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/platform.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/publisher_manager.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/resize.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/runtime_state.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/writer_bridge.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/wizard/__init__.py +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/package.json +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/activate.tsx +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/api/streamingApi.ts +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/constants.ts +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/entry.ts +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/SubModal.tsx +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/types.ts +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/tsconfig.json +0 -0
- {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/webpack.config.js +0 -0
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toposync-ext-streaming
|
|
3
|
+
Version: 0.1.4
|
|
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
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
1
13
|
# Toposync Streaming Extension
|
|
2
14
|
|
|
3
15
|
Extension ID: `com.toposync.streaming`
|
|
@@ -125,6 +137,11 @@ Stored under the extension settings as `engine`:
|
|
|
125
137
|
- `mediamtx_version`: selects which MediaMTX version to download/install.
|
|
126
138
|
- `webrtc_ice_servers`: optional list of `stun:` / `turn:` / `turns:` URLs for NAT traversal.
|
|
127
139
|
|
|
140
|
+
Additional WebRTC runtime knobs are available as environment variables for container/add-on deployments:
|
|
141
|
+
- `TOPOSYNC_STREAMING_WEBRTC_ADDITIONAL_HOSTS`: comma-separated LAN/public hosts advertised to WebRTC clients.
|
|
142
|
+
- `TOPOSYNC_STREAMING_WEBRTC_LOCAL_UDP_ADDRESS`: static ICE UDP bind address, for example `:18762`.
|
|
143
|
+
- `TOPOSYNC_STREAMING_WEBRTC_LOCAL_TCP_ADDRESS`: optional static ICE TCP bind address when UDP is blocked.
|
|
144
|
+
|
|
128
145
|
## Engine distribution (on-demand download)
|
|
129
146
|
|
|
130
147
|
This repo does not ship MediaMTX binaries. The backend downloads the correct release asset at runtime when the engine is started.
|
|
@@ -187,6 +204,7 @@ Source of truth:
|
|
|
187
204
|
- `rtsp: true` with `rtspTransports: [udp, tcp]`
|
|
188
205
|
- `hls: true` with `hlsVariant: mpegts`
|
|
189
206
|
- `webrtc: true` with WHEP enabled at `/<path>/whep`
|
|
207
|
+
- `webrtcAdditionalHosts` and static ICE addresses when configured through environment variables.
|
|
190
208
|
|
|
191
209
|
## Security considerations
|
|
192
210
|
|
|
@@ -629,6 +647,7 @@ To enable LAN viewers:
|
|
|
629
647
|
|
|
630
648
|
Notes:
|
|
631
649
|
- `GET /api/streams/transmissions/{id}/urls` chooses the host component based on the current request host when `expose_to_lan` is enabled.
|
|
650
|
+
- 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`.
|
|
632
651
|
- MediaMTX still restricts publishing and API access to localhost IPs; LAN exposure is for viewer playback only.
|
|
633
652
|
|
|
634
653
|
## Troubleshooting
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: toposync-ext-streaming
|
|
3
|
-
Version: 0.1.2
|
|
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
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
|
|
13
1
|
# Toposync Streaming Extension
|
|
14
2
|
|
|
15
3
|
Extension ID: `com.toposync.streaming`
|
|
@@ -137,6 +125,11 @@ Stored under the extension settings as `engine`:
|
|
|
137
125
|
- `mediamtx_version`: selects which MediaMTX version to download/install.
|
|
138
126
|
- `webrtc_ice_servers`: optional list of `stun:` / `turn:` / `turns:` URLs for NAT traversal.
|
|
139
127
|
|
|
128
|
+
Additional WebRTC runtime knobs are available as environment variables for container/add-on deployments:
|
|
129
|
+
- `TOPOSYNC_STREAMING_WEBRTC_ADDITIONAL_HOSTS`: comma-separated LAN/public hosts advertised to WebRTC clients.
|
|
130
|
+
- `TOPOSYNC_STREAMING_WEBRTC_LOCAL_UDP_ADDRESS`: static ICE UDP bind address, for example `:18762`.
|
|
131
|
+
- `TOPOSYNC_STREAMING_WEBRTC_LOCAL_TCP_ADDRESS`: optional static ICE TCP bind address when UDP is blocked.
|
|
132
|
+
|
|
140
133
|
## Engine distribution (on-demand download)
|
|
141
134
|
|
|
142
135
|
This repo does not ship MediaMTX binaries. The backend downloads the correct release asset at runtime when the engine is started.
|
|
@@ -199,6 +192,7 @@ Source of truth:
|
|
|
199
192
|
- `rtsp: true` with `rtspTransports: [udp, tcp]`
|
|
200
193
|
- `hls: true` with `hlsVariant: mpegts`
|
|
201
194
|
- `webrtc: true` with WHEP enabled at `/<path>/whep`
|
|
195
|
+
- `webrtcAdditionalHosts` and static ICE addresses when configured through environment variables.
|
|
202
196
|
|
|
203
197
|
## Security considerations
|
|
204
198
|
|
|
@@ -641,6 +635,7 @@ To enable LAN viewers:
|
|
|
641
635
|
|
|
642
636
|
Notes:
|
|
643
637
|
- `GET /api/streams/transmissions/{id}/urls` chooses the host component based on the current request host when `expose_to_lan` is enabled.
|
|
638
|
+
- 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`.
|
|
644
639
|
- MediaMTX still restricts publishing and API access to localhost IPs; LAN exposure is for viewer playback only.
|
|
645
640
|
|
|
646
641
|
## Troubleshooting
|
|
@@ -20,7 +20,7 @@ from toposync.runtime.config_store import (
|
|
|
20
20
|
ProcessingServer,
|
|
21
21
|
)
|
|
22
22
|
from toposync.runtime.pipelines.compiler import GraphCompileError, PipelineGraphCompiler
|
|
23
|
-
from toposync.runtime.pipelines.templates import safe_pipeline_name
|
|
23
|
+
from toposync.runtime.pipelines.templates import camera_names_by_id, safe_pipeline_name
|
|
24
24
|
from toposync.runtime.services import ServiceRegistry
|
|
25
25
|
|
|
26
26
|
from ..streaming.engine_manager import MediaMtxEngineManager
|
|
@@ -1277,7 +1277,10 @@ def create_streaming_router() -> APIRouter:
|
|
|
1277
1277
|
else:
|
|
1278
1278
|
suggested = suggested_streaming_wizard_pipeline_name(
|
|
1279
1279
|
transmission_id=transmission.id,
|
|
1280
|
+
transmission_name=transmission.name,
|
|
1281
|
+
transmission_path=transmission.path,
|
|
1280
1282
|
camera_id=resolved_camera_id,
|
|
1283
|
+
camera_name=camera_names_by_id(app_settings.extensions).get(resolved_camera_id),
|
|
1281
1284
|
preset_id=body.preset_id,
|
|
1282
1285
|
)
|
|
1283
1286
|
pipeline_name = _unique_pipeline_name(suggested, existing_names=existing_names)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see 703.js.LICENSE.txt */
|
|
2
|
+
"use strict";(self.webpackChunk_toposync_extension_streaming_ui=self.webpackChunk_toposync_extension_streaming_ui||[]).push([[703],{87(e,s,t){t.d(s,{D:()=>a});const a="com.toposync.streaming"},94(e,s,t){var a=t(496),i=Symbol.for("react.element"),n=(Symbol.for("react.fragment"),Object.prototype.hasOwnProperty),r=a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,o={key:!0,ref:!0,__self:!0,__source:!0};function l(e,s,t){var a,l={},m=null,c=null;for(a in void 0!==t&&(m=""+t),void 0!==s.key&&(m=""+s.key),void 0!==s.ref&&(c=s.ref),s)n.call(s,a)&&!o.hasOwnProperty(a)&&(l[a]=s[a]);if(e&&e.defaultProps)for(a in s=e.defaultProps)void 0===l[a]&&(l[a]=s[a]);return{$$typeof:i,type:e,key:m,ref:c,props:l,_owner:r.current}}s.jsx=l,s.jsxs=l},155(e,s,t){t.d(s,{Y:()=>p});var a=t(870),i=t(496),n=t(901),r=t(697);const o=[{id:"simple_stream",titleKey:"ext.streaming.wizard.presets.simple_stream.title",descriptionKey:"ext.streaming.wizard.presets.simple_stream.desc",fallbackTitle:"Simple stream",fallbackDescription:"camera.source + optional fps reducer + stream.publish_video"},{id:"motion_gate_stream",titleKey:"ext.streaming.wizard.presets.motion_gate_stream.title",descriptionKey:"ext.streaming.wizard.presets.motion_gate_stream.desc",fallbackTitle:"Motion gate stream",fallbackDescription:"camera.source + motion gate + fps reducer + stream.publish_video"},{id:"detection_stream",titleKey:"ext.streaming.wizard.presets.detection_stream.title",descriptionKey:"ext.streaming.wizard.presets.detection_stream.desc",fallbackTitle:"Detection stream",fallbackDescription:"camera.source + object detection + stream.publish_video"},{id:"tracking_stream",titleKey:"ext.streaming.wizard.presets.tracking_stream.title",descriptionKey:"ext.streaming.wizard.presets.tracking_stream.desc",fallbackTitle:"Tracking stream",fallbackDescription:"camera.source + object tracking + stream.publish_video"},{id:"segmentation_stream",titleKey:"ext.streaming.wizard.presets.segmentation_stream.title",descriptionKey:"ext.streaming.wizard.presets.segmentation_stream.desc",fallbackTitle:"Segmentation stream",fallbackDescription:"camera.source + object segmentation + stream.publish_video"}];function l(e){const s=String(e||"").trim();if(!s)return;const t=Number(s);return Number.isFinite(t)?t:void 0}function m(e){const s=String(e||"").trim();if(!s)return;const t=Number.parseInt(s,10);return Number.isFinite(t)?t:void 0}function c(e){return String(e||"").trim().toLowerCase()||"local"}function d(e){return String(e||"").normalize("NFD").replace(/[\u0300-\u036f]/g,"").toLowerCase().trim()}function g(e){const s=String(e||"").trim();return s.length<=12?s:`${s.slice(0,8)}...`}function u(e,s){const t=String(s||"").trim();return t&&e.some(e=>String(e.id||"").trim()===t)?t:""}function p({open:e,i18n:s,transmission:t,engineRunning:p,processingServers:x,onClose:h,onCreated:_}){const{t:v}=s.useI18n(),b=(0,i.useId)(),f=(0,i.useRef)(null),[y,j]=(0,i.useState)("form"),[N,w]=(0,i.useState)("simple_stream"),[S,C]=(0,i.useState)(""),[k,z]=(0,i.useState)(!1),[T,M]=(0,i.useState)(""),[A,P]=(0,i.useState)([]),[L,B]=(0,i.useState)(!1),[R,E]=(0,i.useState)(null),[F,D]=(0,i.useState)(""),[U,W]=(0,i.useState)(!0),[I,O]=(0,i.useState)("local"),[H,$]=(0,i.useState)("auto"),[q,G]=(0,i.useState)(!1),[X,K]=(0,i.useState)(""),[V,J]=(0,i.useState)("0.01"),[Q,Y]=(0,i.useState)("6"),[Z,ee]=(0,i.useState)("contain"),[se,te]=(0,i.useState)("0"),[ae,ie]=(0,i.useState)("auto"),[ne,re]=(0,i.useState)("0.55"),[oe,le]=(0,i.useState)(!0),[me,ce]=(0,i.useState)(""),[de,ge]=(0,i.useState)(""),[ue,pe]=(0,i.useState)(!1),[xe,he]=(0,i.useState)(null),[_e,ve]=(0,i.useState)(null),be=String(t?.camera_controls?.camera_id||"").trim();(0,i.useEffect)(()=>{e&&(j("form"),w("simple_stream"),C(""),z(!1),M(""),P([]),B(!1),E(null),D(""),W(!0),O(c(t?.host_server_id)),$("auto"),G(!1),K(""),J("0.01"),Y("6"),ee("contain"),te("0"),ie("auto"),re("0.55"),le(!0),ce(""),ge(""),pe(!1),he(null),ve(null))},[e,t?.host_server_id,t?.id]),(0,i.useEffect)(()=>{if(!e)return;const s=new AbortController;return B(!0),E(null),P([]),(async()=>{try{const e=await(0,n.bO)(s.signal);if(s.signal.aborted)return;const a=Array.isArray(e.cameras)?e.cameras:[];P(a),C(e=>{if(0===a.length)return"";const s=String(e||"").trim();return u(a,s)||function(e,s,t){const a=u(e,s);if(a)return a;const i=[t?.name,t?.path].map(e=>d(String(e||""))).filter(Boolean);if(i.length>0){const s=e.find(e=>{const s=d(String(e.name||"")),t=d(String(e.id||""));return s&&i.includes(s)||t&&i.includes(t)});if(s)return String(s.id||"").trim()}return 1===e.length?String(e[0]?.id||"").trim():""}(a,be,t)})}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;E(e instanceof Error?e.message:String(e))}finally{s.signal.aborted||B(!1)}})(),()=>s.abort()},[e,t?.id,t?.name,t?.path,be]),(0,i.useEffect)(()=>{if(k)return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)};function e(e){const s=f.current;s&&e.target instanceof Node&&(s.contains(e.target)||z(!1))}},[k]);const fe=(0,i.useMemo)(()=>t?t.name?.trim()||t.path?.trim()||t.id:"",[t]),ye=(0,i.useMemo)(()=>o.find(e=>e.id===N)??o[0],[N]),je=(0,i.useMemo)(()=>A.find(e=>String(e.id||"").trim()===S.trim())??null,[S,A]),Ne=(0,i.useMemo)(()=>{const e=new Map;for(const s of A){const t=String(s.name||"").trim();if(!t)continue;const a=d(t);e.set(a,(e.get(a)??0)+1)}return e},[A]),we=(0,i.useMemo)(()=>{const e=d(T);return e?A.filter(s=>d(`${s.name||""} ${s.id||""}`).includes(e)):A},[T,A]),Se=(0,i.useMemo)(()=>function(e){const s=e.find(e=>"local"===c(e.id))??null,t=e.filter(e=>"local"!==c(e.id)).sort((e,s)=>String(e.id||"").localeCompare(String(s.id||"")));return s?[s,...t]:[{id:"local",name:"Local",kind:"inprocess",url:""},...t]}(Array.isArray(x)?x:[]),[x]),Ce=(0,i.useMemo)(()=>{const e=new Set(["local"]);for(const s of Se)e.add(c(s.id));return e},[Se]),ke=c(t?.host_server_id),ze=c(I),Te=ke!==ze;function Me(e){const s=e.split(",").map(e=>e.trim().toLowerCase()).filter(Boolean);if(0!==s.length)return Array.from(new Set(s))}function Ae(e){return String(e?.name||"").trim()||v("ext.streaming.wizard.camera_unnamed",{},"Unnamed camera")}function Pe(e,s){const t=String(e?.id||s||"").trim();if(!t)return"";const a=String(e?.name||"").trim();return a?(Ne.get(d(a))??0)>1?`ID ${g(t)}`:"":`ID ${g(t)}`}return(0,a.jsxs)(r.y,{open:e,title:v("ext.streaming.wizard.title",{},"Criar fluxo para transmissão"),closeAriaLabel:v("core.actions.close",{},"Close"),onClose:()=>{ue||h()},children:["form"===y?(0,a.jsxs)("div",{className:"streamingWizard",children:[(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:fe}),(0,a.jsx)("div",{className:"cardMeta",children:t?.id||""}),p?null:(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:8},children:v("ext.streaming.wizard.engine_warning",{},"A engine de streaming está parada. Você pode criar o fluxo agora e iniciar a engine depois.")})]})}),L?(0,a.jsx)("div",{className:"settingsStatusMuted streamingWizardFeedback",children:v("ext.streaming.wizard.loading_cameras",{},"Carregando câmeras…")}):null,R?(0,a.jsx)("div",{className:"errorText streamingWizardFeedback",children:R}):null,xe?(0,a.jsx)("div",{className:"errorText streamingWizardFeedback",children:xe}):null,(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.camera",{},"Câmera")}),(0,a.jsxs)("div",{className:"streamingCameraPicker",ref:f,children:[(0,a.jsxs)("button",{type:"button",className:"input streamingCameraPickerButton","aria-haspopup":"listbox","aria-expanded":k,"aria-controls":b,onClick:()=>{M(""),z(e=>!e)},onKeyDown:e=>{"ArrowDown"===e.key&&(e.preventDefault(),M(""),z(!0)),"Escape"===e.key&&z(!1)},disabled:L||0===A.length,children:[(0,a.jsxs)("span",{className:"streamingCameraPickerButtonText",children:[(0,a.jsx)("span",{className:"streamingCameraPickerName",children:je?Ae(je):L?v("ext.streaming.wizard.loading_cameras",{},"Loading cameras..."):v("ext.streaming.wizard.camera_placeholder",{},"Select a camera")}),je||S.trim()?(0,a.jsx)("span",{className:"streamingCameraPickerMeta",children:Pe(je,S)}):null]}),(0,a.jsx)("i",{className:"fa-solid "+(k?"fa-chevron-up":"fa-chevron-down"),"aria-hidden":"true"})]}),k?(0,a.jsxs)("div",{className:"streamingCameraPickerPopover",children:[(0,a.jsx)("input",{className:"input streamingCameraPickerSearch",value:T,onChange:e=>M(e.target.value),onKeyDown:e=>{"Escape"===e.key&&(e.preventDefault(),z(!1))},placeholder:v("ext.streaming.wizard.camera_search",{},"Search by name or ID"),autoFocus:!0}),(0,a.jsx)("div",{className:"streamingCameraPickerList",id:b,role:"listbox","aria-label":v("ext.streaming.wizard.camera",{},"Camera"),children:we.length>0?we.map(e=>{const s=String(e.id||"").trim(),t=s===S.trim(),i=Pe(e);return(0,a.jsxs)("button",{type:"button",className:"streamingCameraPickerOption"+(t?" isSelected":""),role:"option","aria-selected":t,onClick:()=>{C(s),z(!1),M("")},children:[(0,a.jsxs)("span",{className:"streamingCameraPickerOptionMain",children:[(0,a.jsx)("span",{className:"streamingCameraPickerOptionName",children:Ae(e)}),i?(0,a.jsx)("span",{className:"streamingCameraPickerOptionMeta",children:i}):null]}),t?(0,a.jsx)("i",{className:"fa-solid fa-check","aria-hidden":"true"}):null]},s)}):(0,a.jsx)("div",{className:"streamingCameraPickerEmpty",children:v("ext.streaming.wizard.camera_no_results",{},"No cameras match your search.")})})]}):null]}),0!==A.length||L?null:(0,a.jsx)("div",{className:"cardMeta",children:v("ext.streaming.wizard.camera_empty",{},"No cameras found. Add a camera in Settings > Cameras.")})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.preset",{},"Preset")}),(0,a.jsx)("select",{className:"input",value:N,onChange:e=>w(e.target.value),children:o.map(e=>(0,a.jsx)("option",{value:e.id,children:v(e.titleKey,{},e.fallbackTitle)},e.id))}),(0,a.jsx)("div",{className:"label",children:v(ye.descriptionKey,{},ye.fallbackDescription)})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10},children:[(0,a.jsxs)("div",{className:"field",style:{flex:1,minWidth:220},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.pipeline_name",{},"Nome do fluxo (opcional)")}),(0,a.jsx)("input",{className:"input",value:F,onChange:e=>D(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.processing_server",{},"Processing server")}),(0,a.jsxs)("select",{className:"input",value:ze,onChange:e=>O(c(e.target.value)),children:[Se.map(e=>{const s=c(e.id),t=String(e.name||"").trim(),i="local"===s?v("ext.streaming.processing_servers.local_label",{},"local (this machine)"):t?`${s} (${t})`:s;return(0,a.jsx)("option",{value:s,children:i},s)}),Ce.has(ze)?null:(0,a.jsx)("option",{value:ze,children:ze})]})]})]}),Te?(0,a.jsx)("div",{className:"errorText",children:v("ext.streaming.wizard.host_mismatch_inline",{transmissionHost:ke,pipelineHost:ze},`Transmission is hosted on ${ke} and pipeline is on ${ze}. They must match.`)}):null,(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10},children:[(0,a.jsxs)("div",{className:"field",style:{width:170},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.fps",{},"FPS limit")}),(0,a.jsx)("input",{className:"input",value:X,onChange:e=>K(e.target.value),placeholder:"5"})]}),(0,a.jsxs)("div",{className:"field",style:{width:170},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.writer_priority",{},"Writer priority")}),(0,a.jsx)("input",{className:"input",value:se,onChange:e=>te(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.source_backend",{},"Backend da câmera")}),(0,a.jsxs)("select",{className:"input",value:H,onChange:e=>$(e.target.value),children:[(0,a.jsx)("option",{value:"auto",children:v("ext.streaming.wizard.source_backend.option.auto",{},"Auto")}),(0,a.jsx)("option",{value:"opencv",children:v("ext.streaming.wizard.source_backend.option.opencv",{},"OpenCV")}),(0,a.jsx)("option",{value:"ffmpeg",children:v("ext.streaming.wizard.source_backend.option.ffmpeg",{},"FFmpeg")})]})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.bypass_mode",{},"Bypass mode")}),(0,a.jsxs)("select",{className:"input",value:ae,onChange:e=>ie(e.target.value),children:[(0,a.jsx)("option",{value:"auto",children:v("ext.streaming.wizard.bypass_mode.option.auto",{},"Auto")}),(0,a.jsx)("option",{value:"force_on",children:v("ext.streaming.wizard.bypass_mode.option.force_on",{},"Force on")}),(0,a.jsx)("option",{value:"force_off",children:v("ext.streaming.wizard.bypass_mode.option.force_off",{},"Force off")})]})]})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10},children:[(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.resize_mode",{},"Resize mode")}),(0,a.jsxs)("select",{className:"input",value:Z,onChange:e=>ee(e.target.value),children:[(0,a.jsx)("option",{value:"contain",children:v("ext.streaming.wizard.resize_mode.option.contain",{},"Contain")}),(0,a.jsx)("option",{value:"none",children:v("ext.streaming.wizard.resize_mode.option.none",{},"No resize")})]})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.motion_sensitivity",{},"Motion sensitivity")}),(0,a.jsx)("input",{className:"input",value:V,onChange:e=>J(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.motion_hold",{},"Motion hold (s)")}),(0,a.jsx)("input",{className:"input",value:Q,onChange:e=>Y(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",style:{width:180},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.yolo_conf",{},"Vision confidence")}),(0,a.jsx)("input",{className:"input",value:ne,onChange:e=>re(e.target.value)})]})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsxs)("label",{className:"rowWrap",style:{gap:10},children:[(0,a.jsx)("input",{type:"checkbox",checked:oe,onChange:e=>le(e.target.checked)}),(0,a.jsx)("span",{className:"cardMeta",children:v("ext.streaming.wizard.yolo_filter_enabled",{},"Filter frames after vision (recommended)")})]}),(0,a.jsx)("div",{className:"cardMeta",style:{marginLeft:28},children:v("ext.streaming.wizard.yolo_filter_enabled.hint",{},"When disabled, the pipeline still runs vision inference but keeps all frames.")})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10},children:[(0,a.jsxs)("div",{className:"field",style:{flex:1,minWidth:260},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.detection_categories",{},"Detection categories (csv)")}),(0,a.jsx)("input",{className:"input",value:me,onChange:e=>ce(e.target.value),placeholder:"person,car"})]}),(0,a.jsxs)("div",{className:"field",style:{flex:1,minWidth:260},children:[(0,a.jsx)("label",{className:"label",children:v("ext.streaming.wizard.tracking_categories",{},"Tracking categories (csv)")}),(0,a.jsx)("input",{className:"input",value:de,onChange:e=>ge(e.target.value),placeholder:"person,car"})]})]}),(0,a.jsx)("div",{className:"field",children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:10},children:[(0,a.jsx)("input",{type:"checkbox",checked:U,onChange:e=>W(e.target.checked)}),(0,a.jsx)("span",{className:"cardMeta",children:v("ext.streaming.wizard.enabled",{},"Fluxo habilitado após criação")})]})}),(0,a.jsx)("div",{className:"field",children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:10},children:[(0,a.jsx)("input",{type:"checkbox",checked:q,onChange:e=>G(e.target.checked)}),(0,a.jsx)("span",{className:"cardMeta",children:v("ext.streaming.wizard.use_fps_reducer",{},"Inserir step core.fps_reducer")})]})})]})}),(0,a.jsxs)("div",{className:"rowWrap",style:{marginTop:14,justifyContent:"flex-end",gap:10},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:h,disabled:ue,children:v("core.actions.cancel",{},"Cancelar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",onClick:()=>{!async function(){if(t)if(S.trim())if(Ce.has(ze))if(Te)he(v("ext.streaming.wizard.errors.host_mismatch",{transmissionHost:ke,pipelineHost:ze},`Transmission is hosted on '${ke}'. Select the same processing server for the pipeline.`));else{pe(!0),he(null),z(!1);try{const e=await(0,n.A$)({transmission_id:t.id,camera_id:S.trim(),preset_id:N,optional_parameters:{pipeline_name:F.trim()||void 0,enabled:U,processing_server_id:ze,source_backend:H,use_fps_reducer:q,fps_limit:l(X),motion_sensitivity:l(V),motion_hold_seconds:l(Q),resize_mode:Z,writer_priority:m(se),bypass_mode:ae,yolo_confidence_threshold:l(ne),yolo_filter_enabled:oe,detection_categories:Me(me),tracking_categories:Me(de)}});ve(e),j("done"),_(e)}catch(e){he(e instanceof Error?e.message:String(e))}finally{pe(!1)}}else he(v("ext.streaming.wizard.errors.invalid_processing_server",{serverId:ze},`Invalid processing server: ${ze}`));else he(v("ext.streaming.wizard.errors.select_camera",{},"Select a camera."))}()},disabled:ue||!S.trim()||Te||!Ce.has(ze),children:ue?v("ext.streaming.wizard.creating",{},"Criando…"):v("ext.streaming.wizard.create",{},"Criar fluxo")})]})]}):null,"done"===y&&_e?(0,a.jsxs)("div",{className:"streamingWizard",children:[(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:v("ext.streaming.wizard.done_title",{},"Fluxo criado")}),(0,a.jsxs)("div",{className:"cardMeta",children:[v("ext.streaming.wizard.pipeline_created_name",{},"Fluxo"),": ",_e.pipeline_name]}),Array.isArray(_e.warnings)&&_e.warnings.length>0?(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:8},children:_e.warnings.join(" ")}):null]})}),(0,a.jsxs)("div",{className:"rowWrap",style:{marginTop:14,justifyContent:"flex-end",gap:10},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:h,children:v("core.actions.close",{},"Fechar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",onClick:()=>{!function(){if("undefined"==typeof window)return;const e="/settings/pipelines";window.location.pathname!==e&&(window.history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate")))}(),h()},children:v("ext.streaming.wizard.open_pipelines",{},"Abrir Fluxos")})]})]}):null]})}},323(e,s,t){t.d(s,{w:()=>v});var a=t(870),i=t(496),n=t(901),r=t(87),o=t(697),l=t(155);function m(e,s){const t=Number.parseInt(String(e||"").trim(),10);return Number.isFinite(t)?t:s}function c(e){const s=String(e||"").trim();if(!s)return null;const t=Number.parseInt(s,10);return Number.isFinite(t)?t:null}function d(e){const s=String(e||"").trim().toLowerCase();return s?Array.from(s).map(e=>/[a-z0-9_-]/.test(e)?e:"-").join("").replace(/-+/g,"-").replace(/^[-_]+|[-_]+$/g,""):""}function g(e){return"function"==typeof structuredClone?structuredClone(e):JSON.parse(JSON.stringify(e))}function u(e){try{const s=globalThis.crypto;if(s?.randomUUID)return`${e}_${s.randomUUID()}`}catch{}return`${e}_${Date.now()}_${Math.floor(1e6*Math.random())}`}function p(e){const s=String(e||"").replace(/\r/g,"").split(/[\n,]/g),t=[],a=new Set;for(const e of s){const s=String(e||"").trim();if(!s)continue;const i=s.toLowerCase();(i.startsWith("stun:")||i.startsWith("turn:")||i.startsWith("turns:"))&&(a.has(i)||(a.add(i),t.push(s)))}return t}function x(e){if(!Number.isFinite(e)||!e||e<1)return"-";const s=Math.max(0,Math.floor(e)),t=Math.floor(s/3600),a=Math.floor(s%3600/60),i=s%60;return t>0?`${t}h ${a}m`:a>0?`${a}m ${i}s`:`${i}s`}function h(e){return{id:u("output"),protocol:e,enabled:!0,resolution:{width:1280,height:720},fps_limit:12,bitrate_kbps:null,latency_profile:"normal",authentication:{enabled:!1,username:"",password:""}}}function _(e){return String(e||"").trim().toLowerCase()||"local"}function v(){return{id:r.D,icon:"tower-broadcast",name:{key:"ext.streaming.settings.name",fallback:"Transmissões"},description:{key:"ext.streaming.settings.desc"},render:({i18n:e,settings:s})=>(0,a.jsx)(b,{i18n:e,settings:s})}}function b({i18n:e,settings:s}){const{t}=e.useI18n(),[r,u]=(0,i.useState)(!0),[v,b]=(0,i.useState)(null),[f,y]=(0,i.useState)(null),[j,N]=(0,i.useState)(!0),[w,S]=(0,i.useState)(!1),[C,k]=(0,i.useState)(null),[z,T]=(0,i.useState)(null),[M,A]=(0,i.useState)(null),[P,L]=(0,i.useState)(!0),[B,R]=(0,i.useState)(null),[E,F]=(0,i.useState)(null),[D,U]=(0,i.useState)(null),[W,I]=(0,i.useState)(!1),[O,H]=(0,i.useState)(!1),[$,q]=(0,i.useState)(!0),[G,X]=(0,i.useState)(null),[K,V]=(0,i.useState)([]),[J,Q]=(0,i.useState)(""),[Y,Z]=(0,i.useState)(null),[ee,se]=(0,i.useState)(null),[te,ae]=(0,i.useState)(!1),[ie,ne]=(0,i.useState)(null),[re,oe]=(0,i.useState)(!1),[le,me]=(0,i.useState)(!1),[ce,de]=(0,i.useState)(null),[ge,ue]=(0,i.useState)({}),[pe,xe]=(0,i.useState)(null),[he,_e]=(0,i.useState)(null),[ve,be]=(0,i.useState)(!0),[fe,ye]=(0,i.useState)(null),[je,Ne]=(0,i.useState)([{id:"local",name:"Local",kind:"inprocess",url:""}]),[we,Se]=(0,i.useState)(!1),[Ce,ke]=(0,i.useState)(!1),[ze,Te]=(0,i.useState)(null),[Me,Ae]=(0,i.useState)(""),[Pe,Le]=(0,i.useState)(""),[Be,Re]=(0,i.useState)("local"),[Ee,Fe]=(0,i.useState)("hls"),[De,Ue]=(0,i.useState)("1280"),[We,Ie]=(0,i.useState)("720"),[Oe,He]=(0,i.useState)("12"),[$e,qe]=(0,i.useState)(!1),[Ge,Xe]=(0,i.useState)(""),[Ke,Ve]=(0,i.useState)(!1),[Je,Qe]=(0,i.useState)(null),[Ye,Ze]=(0,i.useState)([]),[es,ss]=(0,i.useState)(!1),[ts,as]=(0,i.useState)(!1),[is,ns]=(0,i.useState)(null),[rs,os]=(0,i.useState)(null),ls=(0,i.useMemo)(()=>{const e=s.transmissions;return Array.isArray(e)?e.length:0},[s.transmissions]),ms=K.length>0?K.length:ls,cs=(0,i.useCallback)(async e=>{u(!0),y(null);try{const s=await(0,n.m9)(e);b(s)}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;b(null),y(e instanceof Error?e.message:String(e))}finally{e?.aborted||u(!1)}},[]),ds=(0,i.useCallback)(async e=>{N(!0),A(null);try{const s=await(0,n.JB)(e);T(s)}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;T(null),A(e instanceof Error?e.message:String(e))}finally{e?.aborted||N(!1)}},[]),gs=(0,i.useCallback)(async e=>{L(!0),R(null);try{const s=await(0,n.WO)(e);if(e?.aborted)return;F(s),U(g(s.engine??{})),I(!1)}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;F(null),R(e instanceof Error?e.message:String(e))}finally{e?.aborted||L(!1)}},[]),us=(0,i.useCallback)(async e=>{q(!0),X(null);try{const s=await(0,n.I$)(e);V(s)}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;V([]),X(e instanceof Error?e.message:String(e))}finally{e?.aborted||q(!1)}},[]),ps=(0,i.useCallback)(async e=>{be(!0),ye(null);try{const s=await(0,n.cr)(e);if(e?.aborted)return;Ne(function(e){const s=e.find(e=>"local"===_(e.id))??null,t=e.filter(e=>"local"!==_(e.id)).sort((e,s)=>String(e.id||"").localeCompare(String(s.id||"")));return s?[s,...t]:[{id:"local",name:"Local",kind:"inprocess",url:""},...t]}(Array.isArray(s)?s:[]))}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;Ne([{id:"local",name:"Local",kind:"inprocess",url:""}]),ye(e instanceof Error?e.message:String(e))}finally{e?.aborted||be(!1)}},[]),xs=(0,i.useCallback)(async e=>{Ve(!0),Qe(null);try{const s=await(0,n.bO)(e);if(e?.aborted)return;const t=Array.isArray(s.cameras)?s.cameras:[];Ze(t)}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)return;Ze([]),Qe(e instanceof Error?e.message:String(e))}finally{e?.aborted||Ve(!1)}},[]);(0,i.useEffect)(()=>{const e=new AbortController;return cs(e.signal),ds(e.signal),gs(e.signal),us(e.signal),ps(e.signal),xs(e.signal),()=>e.abort()},[xs,ds,cs,ps,gs,us]),(0,i.useEffect)(()=>{const e=window.setInterval(()=>{"hidden"!==document.visibilityState&&ds()},5e3);return()=>window.clearInterval(e)},[ds]),(0,i.useEffect)(()=>{Y&&K.some(e=>e.id===Y)||Z(K[0]?.id??null)},[Y,K]);const hs=(0,i.useMemo)(()=>Y?K.find(e=>e.id===Y)??null:null,[Y,K]);(0,i.useEffect)(()=>{if(!hs)return ne(null),oe(!1),void de(null);re||(ne(g(hs)),de(null))},[hs,re]);const _s=(0,i.useMemo)(()=>{const e=J.trim().toLowerCase();return e?K.filter(s=>{const t=String(s.name||"").trim().toLowerCase(),a=String(s.path||"").trim().toLowerCase(),i=String(s.id||"").trim().toLowerCase();return t.includes(e)||a.includes(e)||i.includes(e)}):K},[J,K]),vs=(0,i.useMemo)(()=>{const e=new Set(["local"]);for(const s of je)e.add(_(s.id));return e},[je]);async function bs(e){if("reclaim"!==e||confirm(t("ext.streaming.engine.reclaim_confirm",{},"This will try to stop and cleanup stale MediaMTX processes for this data directory. Continue?"))){S(!0),k(e),A(null);try{const s=await(0,n.hF)(e);T(s),gs()}catch(e){A(e instanceof Error?e.message:String(e)),ds()}finally{S(!1),k(null)}}}async function fs(e){xe(e),X(null);try{const s=await(0,n.pU)(e);ue(t=>({...t,[e]:s}))}catch(e){X(e instanceof Error?e.message:String(e))}finally{xe(null)}}function ys(e){ne(s=>s?{...s,...e}:s),oe(!0),de(null)}function js(e,s){ne(t=>{if(!t)return t;const a=Array.isArray(t.outputs)?t.outputs:[];return{...t,outputs:a.map(t=>t.id===e?{...t,...s}:t)}}),oe(!0),de(null)}function Ns(e){ne(s=>{if(!s)return s;const t=Array.isArray(s.outputs)?s.outputs:[];return{...s,outputs:[...t,h(e)]}}),oe(!0),de(null)}async function ws(){if(ie){me(!0),de(null);try{const e=_(ie.host_server_id);if(!vs.has(e))return void de(t("ext.streaming.errors.invalid_host_server",{serverId:e},`Invalid host server: ${e}`));const s={...ie,id:ie.id||Y||"",host_server_id:e},a=await(0,n.Cs)(s.id,s);V(e=>e.map(e=>e.id===a.id?a:e)),ne(g(a)),oe(!1),fs(a.id)}catch(e){de(e instanceof Error?e.message:String(e))}finally{me(!1)}}}(0,i.useEffect)(()=>{if(!we)return;if(qe(!1),Xe(""),Ye.length>0)return;const e=new AbortController;return xs(e.signal),()=>e.abort()},[we,xs]),(0,i.useEffect)(()=>{we&&(Ge.trim()||0!==Ye.length&&Xe(String(Ye[0]?.id||"").trim()))},[Ye,we,Ge]);const Ss=Y?ge[Y]??null:null,Cs=Boolean(z?.running),ks=Array.isArray(z?.orphan_pids)?z.orphan_pids:[],zs=Cs?"restart":"start",Ts="start"===C?t("ext.streaming.engine.starting",{},"Iniciando…"):"restart"===C?t("ext.streaming.engine.restarting",{},"Reiniciando…"):Cs?t("ext.streaming.engine.restart",{},"Reiniciar"):t("ext.streaming.engine.start",{},"Iniciar"),Ms="stop"===C?t("ext.streaming.engine.stopping",{},"Parando…"):t("ext.streaming.engine.stop",{},"Parar"),As="reclaim"===C?t("ext.streaming.engine.reclaiming",{},"Recuperando…"):t("ext.streaming.engine.reclaim",{},"Recuperar"),Ps="download"===C?t("ext.streaming.engine.downloading",{},"Baixando…"):t("ext.streaming.engine.download",{},"Baixar engine"),Ls="refresh"===C?t("ext.streaming.engine.refreshing",{},"Atualizando…"):t("ext.streaming.engine.refresh",{},"Atualizar");return(0,a.jsxs)("div",{className:"streamingSettingsPanel",children:[(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.settings.title",{},"Transmissões")}),(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.settings.subtitle",{},"Crie transmissões, configure saídas (HLS/RTSP/WebRTC) e gere URLs a partir do MediaMTX.")}),(0,a.jsxs)("div",{className:"streamingQuickSteps",children:[(0,a.jsx)("div",{className:"streamingQuickStepsTitle",children:t("ext.streaming.settings.quickstart",{},"Fluxo Recomendado")}),(0,a.jsxs)("ol",{className:"streamingQuickStepsList",children:[(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_1",{},"Crie uma transmissão com ao menos uma saída.")}),(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_2",{},"Ajuste resolução/FPS/autenticação por saída.")}),(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_3",{},"Salve, carregue URLs e use o wizard para gerar o fluxo.")})]})]})]})}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.settings.transmissions",{},"Transmissões configuradas"),": ",ms]}),r?(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.settings.health.loading")}):null,r||f||"ok"!==v?.status?null:(0,a.jsx)("div",{className:"streamingStatusOk",children:t("ext.streaming.settings.health.ok")}),r||!f&&"ok"===v?.status?null:(0,a.jsxs)("div",{className:"errorText",children:[t("ext.streaming.settings.health.failed")," ",f?`(${f})`:""]}),(0,a.jsx)("div",{className:"rowWrap",children:(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>{cs()},children:t("ext.streaming.settings.health.refresh")})})]})}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.engine.title",{},"Engine (MediaMTX)")}),j?(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.engine.loading")}):null,!j&&z?.running?(0,a.jsx)("div",{className:"streamingStatusOk",children:t("ext.streaming.engine.running")}):null,j||z?.running?null:(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.engine.stopped")}),z?(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridEnginePorts",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.meta_version",{},"Versão"),": ",z.mediamtx_version||"-"]}),(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.meta_pid",{},"PID"),": ",z.pid??"-"]}),(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.meta_uptime",{},"Uptime"),": ",x(z.uptime_seconds)]}),(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.meta_api",{},"API"),": ",z.ports?.api??"-"]})]}):null,z?.bind_host&&z?.ports?(0,a.jsxs)("div",{className:"cardMeta",style:{marginTop:6},children:[z.bind_host," - RTSP ",z.ports.rtsp??"-"," - HLS ",z.ports.hls??"-"," - WebRTC"," ",z.ports.webrtc??"-"]}):null,z?.urls?.rtsp_url?(0,a.jsxs)("div",{className:"cardMeta",style:{marginTop:6},children:[t("ext.streaming.engine.test_rtsp",{},"RTSP (test)"),": ",z.urls.rtsp_url]}):null,z?.urls?.hls_url?(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.test_hls",{},"HLS (test)"),": ",z.urls.hls_url]}):null,z?.urls?.webrtc_url?(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.test_webrtc",{},"WebRTC/WHEP (test)"),": ",z.urls.webrtc_url]}):null,Array.isArray(z?.warnings)&&z.warnings.length>0?(0,a.jsx)("div",{style:{marginTop:6},children:z.warnings.map((e,s)=>(0,a.jsx)("div",{className:"cardMeta",style:s>0?{marginTop:4}:void 0,children:e},`${e}-${s}`))}):null,ks.length>0?(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.engine.orphan_processes",{count:ks.length,pids:ks.join(", ")},`Found ${ks.length} external MediaMTX process(es) for this data directory: ${ks.join(", ")}.`)}):null,z?.last_error?(0,a.jsx)("div",{className:"errorText",style:{marginTop:6},children:z.last_error}):null,M?(0,a.jsx)("div",{className:"errorText",style:{marginTop:6},children:M}):null,(0,a.jsxs)("div",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("button",{className:"primaryButton",type:"button",disabled:w,onClick:()=>{bs(zs)},children:Ts}),(0,a.jsxs)("button",{className:"chipButton",type:"button",disabled:w,onClick:()=>{!async function(){S(!0),k("download"),A(null);try{const e=await(0,n.jP)();T(e),gs()}catch(e){A(e instanceof Error?e.message:String(e)),ds()}finally{S(!1),k(null)}}()},children:[(0,a.jsx)("i",{className:"fa-solid fa-download","aria-hidden":"true"})," ",Ps]}),(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:w||!Cs,onClick:()=>{bs("stop")},children:Ms}),(0,a.jsxs)("button",{className:"chipButton",type:"button",disabled:w,onClick:()=>{bs("reclaim")},children:[(0,a.jsx)("i",{className:"fa-solid fa-broom","aria-hidden":"true"})," ",As]}),(0,a.jsxs)("button",{className:"chipButton",type:"button",disabled:w||"refresh"===C,onClick:()=>{!async function(){k("refresh");try{await ds()}finally{k(null)}}()},children:[(0,a.jsx)("i",{className:"fa-solid fa-rotate-right","aria-hidden":"true"})," ",Ls]})]}),z?(0,a.jsxs)("div",{style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.engine.meta_restarts",{},"Reinícios"),": ",z.restart_count??0]}),z.binary_path?(0,a.jsxs)("div",{className:"cardMeta",style:{marginTop:4},children:[t("ext.streaming.engine.meta_binary",{},"Binário"),": ",z.binary_path]}):null,z.config_path?(0,a.jsxs)("div",{className:"cardMeta",style:{marginTop:4},children:[t("ext.streaming.engine.meta_config",{},"Config"),": ",z.config_path]}):null,z.log_path?(0,a.jsxs)("div",{className:"cardMeta",style:{marginTop:4},children:[t("ext.streaming.engine.meta_log",{},"Log"),": ",z.log_path]}):null]}):null,(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.engine.settings_title",{},"Configuração")}),P?(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.engine.settings_loading",{},"Carregando…")}):null,B?(0,a.jsx)("div",{className:"errorText",children:B}):null,!P&&D?(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"rowWrap",style:{gap:10},children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:Boolean(D.expose_to_lan),onChange:e=>{U(s=>({...s??{},expose_to_lan:e.target.checked})),I(!0)}}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.engine.expose_to_lan",{},"Expor na LAN (0.0.0.0)")})]})}),(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridEnginePorts",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.engine.port_rtsp",{},"RTSP port")}),(0,a.jsx)("input",{className:"input",value:String(D.preferred_ports?.rtsp??""),onChange:e=>{const s=c(e.target.value);U(e=>({...e??{},preferred_ports:{...e?.preferred_ports??{},rtsp:s??void 0}})),I(!0)}})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.engine.port_hls",{},"HLS port")}),(0,a.jsx)("input",{className:"input",value:String(D.preferred_ports?.hls??""),onChange:e=>{const s=c(e.target.value);U(e=>({...e??{},preferred_ports:{...e?.preferred_ports??{},hls:s??void 0}})),I(!0)}})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.engine.port_webrtc",{},"WebRTC port")}),(0,a.jsx)("input",{className:"input",value:String(D.preferred_ports?.webrtc??""),onChange:e=>{const s=c(e.target.value);U(e=>({...e??{},preferred_ports:{...e?.preferred_ports??{},webrtc:s??void 0}})),I(!0)}})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.engine.port_api",{},"API port")}),(0,a.jsx)("input",{className:"input",value:String(D.preferred_ports?.api??""),onChange:e=>{const s=c(e.target.value);U(e=>({...e??{},preferred_ports:{...e?.preferred_ports??{},api:s??void 0}})),I(!0)}})]})]}),(0,a.jsxs)("div",{className:"field",style:{marginTop:10},children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.engine.ice_servers_label",{},"STUN/TURN servers (optional, one per line)")}),(0,a.jsx)("textarea",{className:"input",style:{minHeight:84,padding:"8px 10px"},value:(Bs=D.webrtc_ice_servers,Array.isArray(Bs)&&0!==Bs.length?Bs.map(e=>String(e||"").trim()).filter(Boolean).join("\n"):""),placeholder:"stun:stun.l.google.com:19302\nturn:username:password@turn.example.com:3478?transport=udp",onChange:e=>{U(s=>({...s??{},webrtc_ice_servers:p(e.target.value)})),I(!0)}}),(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.engine.ice_servers_hint",{},"Use only when you need to traverse NAT. On a simple LAN, leave it empty.")})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:8,justifyContent:"flex-end",marginTop:10},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:!W||O,onClick:()=>{E?.engine&&(U(g(E.engine)),I(!1))},children:t("ext.streaming.engine.settings_discard",{},"Descartar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",disabled:!W||O,onClick:()=>{!async function(){if(D){H(!0),R(null);try{const e=await(0,n.eR)({engine:{expose_to_lan:Boolean(D.expose_to_lan),preferred_ports:{rtsp:D.preferred_ports?.rtsp,hls:D.preferred_ports?.hls,api:D.preferred_ports?.api,webrtc:D.preferred_ports?.webrtc},webrtc_ice_servers:Array.isArray(D.webrtc_ice_servers)?D.webrtc_ice_servers:[]}});F(e),U(g(e.engine??{})),I(!1),ds()}catch(e){R(e instanceof Error?e.message:String(e))}finally{H(!1)}}}()},children:O?t("ext.streaming.engine.settings_applying",{},"Aplicando…"):t("ext.streaming.engine.settings_apply",{},"Aplicar")})]})]}):null]})}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsxs)("div",{className:"settingsSplit",children:[(0,a.jsxs)("div",{className:"settingsSplitSidebar",children:[(0,a.jsxs)("div",{className:"settingsSplitToolbar",children:[(0,a.jsx)("input",{className:"input",placeholder:t("ext.streaming.transmissions.search",{},"Buscar transmissões…"),value:J,onChange:e=>Q(e.target.value)}),(0,a.jsx)("button",{className:"iconButton iconButtonPrimary",type:"button","aria-label":t("ext.streaming.transmissions.add",{},"Adicionar transmissão"),onClick:()=>{Te(null),Re("local"),Se(!0)},children:(0,a.jsx)("i",{className:"fa-solid fa-plus","aria-hidden":"true"})})]}),$?(0,a.jsx)("div",{className:"settingsStatusMuted",style:{marginTop:10},children:t("ext.streaming.transmissions.loading",{},"Carregando…")}):null,G?(0,a.jsx)("div",{className:"errorText",style:{marginTop:10},children:G}):null,ve?(0,a.jsx)("div",{className:"settingsStatusMuted",style:{marginTop:10},children:t("ext.streaming.processing_servers.loading",{},"Loading processing servers…")}):null,fe?(0,a.jsx)("div",{className:"errorText",style:{marginTop:10},children:fe}):null,0!==_s.length||$?(0,a.jsx)("div",{className:"settingsList",children:_s.map(e=>{const s=e.id===Y,i=String(e.name||"").trim()||String(e.path||"").trim()||e.id,n=_(e.host_server_id),r=Array.isArray(e.outputs)?e.outputs.length:0,o=t("ext.streaming.transmissions.meta_line",{host:n,path:e.path||"-",outputs:r},`host: ${n} • path: ${e.path||"-"} • outputs: ${r}`);return(0,a.jsx)("button",{type:"button",className:["choiceItem",s?"isSelected":""].filter(Boolean).join(" "),onClick:()=>function(e){if(e!==Y)return re?(se(e),void ae(!0)):void Z(e)}(e.id),children:(0,a.jsxs)("div",{className:"settingsListItemRow",children:[(0,a.jsxs)("div",{className:"settingsListItemMain",children:[(0,a.jsx)("div",{className:"settingsListItemTitle",title:i,children:i}),(0,a.jsx)("div",{className:"settingsListItemMeta",title:o,children:o})]}),e.enabled?null:(0,a.jsx)("span",{className:"pillBadge",title:t("ext.streaming.transmissions.badge_disabled_title",{},"Disabled"),children:t("ext.streaming.transmissions.badge_disabled",{},"off")})]})},e.id)})}):(0,a.jsx)("div",{className:"card",style:{marginTop:10},children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{children:t("ext.streaming.transmissions.empty",{},"Nenhuma transmissão criada.")}),(0,a.jsxs)("ol",{className:"streamingQuickStepsList streamingQuickStepsListCompact",children:[(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_1",{},"Crie uma transmissão com ao menos uma saída.")}),(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_2",{},"Ajuste resolução/FPS/autenticação por saída.")}),(0,a.jsx)("li",{children:t("ext.streaming.settings.quickstart_step_3",{},"Salve, carregue URLs e use o wizard para gerar o fluxo.")})]}),(0,a.jsxs)("button",{className:"primaryButton",type:"button",onClick:()=>{Re("local"),Se(!0)},children:[(0,a.jsx)("i",{className:"fa-solid fa-plus","aria-hidden":"true"})," ",t("ext.streaming.transmissions.add",{},"Adicionar transmissão")]})]})})]}),(0,a.jsx)("div",{className:"settingsSplitMain",children:hs&&ie?(0,a.jsxs)("div",{className:"settingsDetail",children:[(0,a.jsxs)("div",{className:"settingsDetailHeader",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:4},children:ie.name?.trim()||ie.path?.trim()||ie.id}),(0,a.jsxs)("div",{className:"cardMeta",children:["ID: ",ie.id]})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10,justifyContent:"flex-end"},children:[re?(0,a.jsx)("span",{className:"pillBadge",title:t("ext.streaming.transmissions.badge_unsaved_title",{},"Unsaved changes"),children:t("ext.streaming.transmissions.badge_unsaved",{},"pending")}):null,(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:!re||le,onClick:function(){hs&&(ne(g(hs)),oe(!1),de(null),xe(null))},children:t("ext.streaming.transmissions.discard",{},"Descartar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",disabled:!re||le,onClick:()=>{ws()},children:le?t("ext.streaming.transmissions.saving",{},"Salvando…"):t("ext.streaming.transmissions.save",{},"Salvar")}),(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:le,onClick:()=>ss(!0),children:t("ext.streaming.transmissions.delete",{},"Excluir")})]})]}),ce?(0,a.jsx)("div",{className:"errorText",style:{marginTop:10},children:ce}):null,(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.transmissions.basic",{},"Básico")}),(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridTransmissionPrimary",children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.name",{},"Nome")}),(0,a.jsx)("input",{className:"input",value:ie.name??"",onChange:e=>ys({name:e.target.value})})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.path",{},"Path/slug")}),(0,a.jsx)("input",{className:"input",value:ie.path??"",onChange:e=>ys({path:e.target.value})}),(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.path_hint",{},"Use apenas a-z, 0-9, - e _. O servidor normaliza automaticamente.")})]})]}),(0,a.jsx)("div",{className:"rowWrap",style:{gap:14,marginTop:10},children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:Boolean(ie.enabled),onChange:e=>ys({enabled:e.target.checked})}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.transmissions.enabled",{},"Ativa")})]})}),(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridTransmissionSecondary",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.host_server",{},"Host server")}),(0,a.jsxs)("select",{className:"input",value:_(ie.host_server_id),onChange:e=>ys({host_server_id:_(e.target.value)}),children:[je.map(e=>{const s=_(e.id),i=String(e.name||"").trim(),n="local"===s?t("ext.streaming.processing_servers.local_label",{},"local (this machine)"):i?`${s} (${i})`:s;return(0,a.jsx)("option",{value:s,children:n},s)}),vs.has(_(ie.host_server_id))?null:(0,a.jsx)("option",{value:_(ie.host_server_id),children:_(ie.host_server_id)})]}),(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.host_server_hint",{},"A transmissão será hospedada neste processing server.")})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.placeholder",{},"Placeholder")}),(0,a.jsxs)("select",{className:"input",value:ie.placeholder??"gray",onChange:e=>ys({placeholder:e.target.value}),children:[(0,a.jsx)("option",{value:"gray",children:t("ext.streaming.transmissions.placeholder.gray",{},"Gray")}),(0,a.jsx)("option",{value:"black",children:t("ext.streaming.transmissions.placeholder.black",{},"Black")})]})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.arbitration",{},"Arbitragem")}),(0,a.jsxs)("select",{className:"input",value:ie.arbitration??"priority_latest",onChange:e=>ys({arbitration:e.target.value}),children:[(0,a.jsx)("option",{value:"priority_latest",children:t("ext.streaming.transmissions.arbitration.priority_latest",{},"Priority, then latest")}),(0,a.jsx)("option",{value:"latest",children:t("ext.streaming.transmissions.arbitration.latest",{},"Latest writer wins")})]})]})]}),(0,a.jsxs)("div",{style:{marginTop:14},children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.transmissions.camera_controls.title",{},"Controles de câmera")}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10,alignItems:"center"},children:[(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:Boolean(ie.camera_controls?.enabled),disabled:!Boolean(ie.camera_controls?.enabled)&&(Ke||0===Ye.length),onChange:e=>{if(!e.target.checked)return void ys({camera_controls:null});const s=String(ie.camera_controls?.camera_id||"").trim(),t=String(Ye[0]?.id||"").trim();ys({camera_controls:{enabled:!0,camera_id:s||t}})}}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.transmissions.camera_controls.enable",{},"Habilitar controles de câmera")})]}),Ke?(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.transmissions.camera_controls.loading",{},"Carregando câmeras…")}):null]}),(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.camera_controls.hint",{},"Quando habilitado, esta transmissão poderá controlar uma câmera (presets/PTZ) via API.")}),Je?(0,a.jsx)("div",{className:"errorText",style:{marginTop:8},children:Je}):null,Boolean(ie.camera_controls?.enabled)?(0,a.jsxs)("div",{className:"field",style:{marginTop:10},children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.camera_controls.camera",{},"Câmera")}),(0,a.jsxs)("select",{className:"input",value:String(ie.camera_controls?.camera_id||"").trim(),onChange:e=>{ys({camera_controls:{enabled:!0,camera_id:String(e.target.value||"").trim()}})},disabled:0===Ye.length,children:[Ye.map(e=>{const s=String(e.id||"").trim(),t=String(e.name||"").trim()||s;return(0,a.jsx)("option",{value:s,children:t},s)}),(()=>{const e=String(ie.camera_controls?.camera_id||"").trim(),s=Ye.some(s=>String(s.id||"").trim()===e);return!e||s?null:(0,a.jsx)("option",{value:e,children:e})})()]}),0!==Ye.length||Ke?null:(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.camera_controls.empty",{},"Nenhuma câmera encontrada. Cadastre uma câmera em Configurações > Câmeras.")})]}):null]}),vs.has(_(ie.host_server_id))?null:(0,a.jsx)("div",{className:"errorText",style:{marginTop:10},children:t("ext.streaming.transmissions.host_server_missing",{},"This host server no longer exists. Select another one before saving.")})]})}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsxs)("div",{className:"settingsDetailHeader",style:{marginBottom:10},children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:4},children:t("ext.streaming.transmissions.outputs",{},"Saídas")}),(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.transmissions.outputs_hint",{},"Cada saída pode ter resolução/FPS/bitrate diferentes.")})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:8,justifyContent:"flex-end"},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>Ns("hls"),children:t("ext.streaming.outputs.add_hls",{},"+ HLS")}),(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>Ns("rtsp"),children:t("ext.streaming.outputs.add_rtsp",{},"+ RTSP")}),(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>Ns("webrtc"),children:t("ext.streaming.outputs.add_webrtc",{},"+ WebRTC")})]})]}),Array.isArray(ie.outputs)&&0===ie.outputs.length?(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.transmissions.outputs_empty",{},"Nenhuma saída adicionada.")}):null,Array.isArray(ie.outputs)?ie.outputs.map(e=>{const s=e.authentication??{enabled:!1,username:"",password:""},i=e.resolution??{width:1280,height:720};return(0,a.jsx)("div",{className:"card",style:{marginTop:10},children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsxs)("div",{className:"settingsDetailHeader",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"settingsListItemTitle",children:[e.protocol.toUpperCase()," ",(0,a.jsxs)("span",{className:"cardMeta",style:{fontWeight:500},children:["(",i.width??"-","x",i.height??"-",")"]})]}),(0,a.jsxs)("div",{className:"settingsListItemMeta",children:[t("ext.streaming.outputs.output_id",{},"Output ID"),": ",e.id]})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:10,justifyContent:"flex-end"},children:[(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:Boolean(e.enabled),onChange:s=>js(e.id,{enabled:s.target.checked})}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.outputs.enabled",{},"Ativa")})]}),(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>{return s=e.id,ne(e=>{if(!e)return e;const t=Array.isArray(e.outputs)?e.outputs:[];return{...e,outputs:t.filter(e=>e.id!==s)}}),oe(!0),void de(null);var s},children:t("ext.streaming.outputs.remove",{},"Remover")})]})]}),(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridOutputMain",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.protocol",{},"Protocolo")}),(0,a.jsxs)("select",{className:"input",value:e.protocol,onChange:s=>js(e.id,{protocol:s.target.value}),children:[(0,a.jsx)("option",{value:"hls",children:"HLS"}),(0,a.jsx)("option",{value:"rtsp",children:"RTSP"}),(0,a.jsx)("option",{value:"webrtc",children:"WebRTC"})]})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.width",{},"Largura")}),(0,a.jsx)("input",{className:"input",value:String(i.width??""),onChange:s=>{const t=c(s.target.value);if(null===t)return void js(e.id,{resolution:null});const a="number"==typeof i.height&&i.height>0?i.height:720;js(e.id,{resolution:{width:Math.max(1,t),height:Math.max(1,a)}})}})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.height",{},"Altura")}),(0,a.jsx)("input",{className:"input",value:String(i.height??""),onChange:s=>{const t=c(s.target.value);if(null===t)return void js(e.id,{resolution:null});const a="number"==typeof i.width&&i.width>0?i.width:1280;js(e.id,{resolution:{width:Math.max(1,a),height:Math.max(1,t)}})}})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.fps",{},"FPS")}),(0,a.jsx)("input",{className:"input",value:null===e.fps_limit||void 0===e.fps_limit?"":String(e.fps_limit),onChange:s=>js(e.id,{fps_limit:c(s.target.value)})})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.bitrate",{},"Bitrate (kbps)")}),(0,a.jsx)("input",{className:"input",value:null===e.bitrate_kbps||void 0===e.bitrate_kbps?"":String(e.bitrate_kbps),onChange:s=>js(e.id,{bitrate_kbps:c(s.target.value)})})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.latency",{},"Latência")}),(0,a.jsxs)("select",{className:"input",value:e.latency_profile??"normal",onChange:s=>js(e.id,{latency_profile:s.target.value}),children:[(0,a.jsx)("option",{value:"normal",children:t("ext.streaming.outputs.latency_option.normal",{},"Normal")}),(0,a.jsx)("option",{value:"low",children:t("ext.streaming.outputs.latency_option.low",{},"Low")}),(0,a.jsx)("option",{value:"ultra_low",children:t("ext.streaming.outputs.latency_option.ultra_low",{},"Ultra low")})]})]})]}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.outputs.auth",{},"Autenticação (opcional)")}),(0,a.jsx)("div",{className:"rowWrap",style:{gap:14},children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:Boolean(s.enabled),onChange:t=>{const a=t.target.checked;js(e.id,{authentication:{...s,enabled:a}})}}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.outputs.auth_enabled",{},"Habilitar user/senha")})]})}),s.enabled?(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridOutputAuth",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.username",{},"Usuário")}),(0,a.jsx)("input",{className:"input",value:String(s.username??""),onChange:t=>js(e.id,{authentication:{...s,username:t.target.value}})})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.outputs.password",{},"Senha")}),(0,a.jsx)("input",{className:"input",type:"password",value:String(s.password??""),onChange:t=>js(e.id,{authentication:{...s,password:t.target.value}})})]}),(0,a.jsx)("div",{className:"cardMeta streamingOutputAuthNote",children:t("ext.streaming.outputs.auth_note",{},"As credenciais são aplicadas no MediaMTX para leitura/playback deste output.")})]}):null]})},e.id)}):null]})}),(0,a.jsx)("div",{className:"sectionDivider"}),(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsxs)("div",{className:"settingsDetailHeader",style:{marginBottom:10},children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:4},children:t("ext.streaming.transmissions.urls",{},"URLs")}),(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.transmissions.urls_hint",{},"As URLs são geradas pelo engine (MediaMTX).")})]}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:8,justifyContent:"flex-end"},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:pe===ie.id,onClick:()=>{fs(ie.id)},children:pe===ie.id?t("ext.streaming.transmissions.loading_urls",{},"Carregando URLs…"):t("ext.streaming.transmissions.load_urls",{},"Carregar URLs")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",onClick:()=>os(ie),children:t("ext.streaming.wizard.open",{},"Criar fluxo com esta transmissão")})]})]}),z?.running?null:(0,a.jsx)("div",{className:"cardMeta",style:{marginBottom:10},children:t("ext.streaming.transmissions.engine_off_warning",{},"A engine está parada: as URLs existirão, mas não haverá playback até iniciar.")}),re?(0,a.jsx)("div",{className:"cardMeta",style:{marginBottom:10},children:t("ext.streaming.transmissions.urls_save_warning",{},"Salve alterações para garantir que paths/outputs estejam atualizados antes de compartilhar URLs.")}):null,Ss&&Ss.transmission_id===ie.id?(0,a.jsxs)("div",{children:[Ss.outputs.map(e=>(0,a.jsx)("div",{className:"card",style:{marginTop:10},children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{className:"settingsListItemTitle",style:{marginBottom:6},children:e.protocol.toUpperCase()}),(0,a.jsxs)("div",{className:"cardMeta",children:[t("ext.streaming.transmissions.engine_path",{},"Engine path"),": ",e.resolved_engine_path]}),e.requires_auth?(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:e.auth_username?t("ext.streaming.transmissions.auth_required_user",{username:e.auth_username},`Requires authentication (username: ${e.auth_username}).`):t("ext.streaming.transmissions.auth_required",{},"Requires authentication.")}):(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.auth_not_required",{},"No authentication.")}),(0,a.jsxs)("div",{className:"rowWrap",style:{gap:8,marginTop:10},children:[(0,a.jsx)("input",{className:"input",style:{flex:1},value:e.url,readOnly:!0}),(0,a.jsx)("button",{className:"iconButton",type:"button","aria-label":t("ext.streaming.transmissions.copy_url",{},"Copy URL"),onClick:()=>{(async function(e){const s=String(e??"");try{return void await navigator.clipboard.writeText(s)}catch{}const t=document.createElement("textarea");t.value=s,t.style.position="fixed",t.style.top="-9999px",t.style.left="-9999px",t.setAttribute("readonly","true"),document.body.appendChild(t),t.select();try{document.execCommand("copy")}finally{document.body.removeChild(t)}})(e.url).then(()=>{_e(e.url),window.setTimeout(()=>_e(null),1200)})},children:(0,a.jsx)("i",{className:"fa-solid fa-copy","aria-hidden":"true"})})]}),he===e.url?(0,a.jsx)("div",{className:"streamingStatusOk",style:{marginTop:8},children:t("ext.streaming.transmissions.copied",{},"Copiado!")}):null]})},`${Ss.transmission_id}-${e.output_id}`)),Array.isArray(Ss.warnings)&&Ss.warnings.length>0?(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:10},children:Ss.warnings.join(" ")}):null]}):(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.transmissions.urls_empty",{},"Carregue as URLs para visualizar aqui.")})]})})]}):(0,a.jsx)("div",{className:"card",children:(0,a.jsxs)("div",{className:"cardBody",children:[(0,a.jsx)("div",{children:t("ext.streaming.transmissions.select",{},"Selecione uma transmissão para editar.")}),(0,a.jsx)("div",{className:"cardMeta",children:t("ext.streaming.transmissions.select_hint",{},"Dica: comece criando uma transmissão e depois abra o wizard para gerar o fluxo.")}),(0,a.jsxs)("button",{className:"primaryButton",type:"button",onClick:()=>{Re("local"),Se(!0)},children:[(0,a.jsx)("i",{className:"fa-solid fa-plus","aria-hidden":"true"})," ",t("ext.streaming.transmissions.add",{},"Adicionar transmissão")]})]})})})]}),(0,a.jsxs)(o.y,{open:we,title:t("ext.streaming.transmissions.create",{},"Criar transmissão"),closeAriaLabel:t("core.actions.close",{},"Close"),onClose:()=>{Ce||Se(!1)},children:[ze?(0,a.jsx)("div",{className:"errorText",style:{marginBottom:10},children:ze}):null,(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridCreatePrimary",children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.name",{},"Nome")}),(0,a.jsx)("input",{className:"input",value:Me,onChange:e=>Ae(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.path",{},"Path/slug")}),(0,a.jsx)("input",{className:"input",value:Pe,onChange:e=>Le(e.target.value),placeholder:d(Me)||"stream"})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.host_server",{},"Host server")}),(0,a.jsx)("select",{className:"input",value:_(Be),onChange:e=>Re(_(e.target.value)),children:je.map(e=>{const s=_(e.id),i=String(e.name||"").trim(),n="local"===s?t("ext.streaming.processing_servers.local_label",{},"local (this machine)"):i?`${s} (${i})`:s;return(0,a.jsx)("option",{value:s,children:n},s)})})]})]}),(0,a.jsxs)("div",{className:"streamingFormGrid streamingFormGridCreateOutput",style:{marginTop:10},children:[(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.protocol",{},"Saída")}),(0,a.jsxs)("select",{className:"input",value:Ee,onChange:e=>Fe(e.target.value),children:[(0,a.jsx)("option",{value:"hls",children:"HLS"}),(0,a.jsx)("option",{value:"rtsp",children:"RTSP"}),(0,a.jsx)("option",{value:"webrtc",children:"WebRTC"})]})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.width",{},"Largura")}),(0,a.jsx)("input",{className:"input",value:De,onChange:e=>Ue(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.height",{},"Altura")}),(0,a.jsx)("input",{className:"input",value:We,onChange:e=>Ie(e.target.value)})]}),(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.fps",{},"FPS")}),(0,a.jsx)("input",{className:"input",value:Oe,onChange:e=>He(e.target.value)})]})]}),(0,a.jsx)("div",{className:"sectionDivider",style:{marginTop:14}}),(0,a.jsx)("div",{className:"modalSectionTitle",style:{marginBottom:6},children:t("ext.streaming.transmissions.camera_controls.title",{},"Controles de câmera")}),(0,a.jsx)("div",{className:"rowWrap",style:{gap:10},children:(0,a.jsxs)("label",{className:"rowWrap",style:{gap:8},children:[(0,a.jsx)("input",{type:"checkbox",checked:$e,onChange:e=>qe(e.target.checked)}),(0,a.jsx)("span",{className:"cardMeta",children:t("ext.streaming.transmissions.camera_controls.enable",{},"Habilitar controles de câmera")})]})}),(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.camera_controls.hint",{},"Quando habilitado, esta transmissão poderá controlar uma câmera (presets/PTZ) via API.")}),$e?(0,a.jsxs)("div",{style:{marginTop:10},children:[Ke?(0,a.jsx)("div",{className:"settingsStatusMuted",children:t("ext.streaming.transmissions.camera_controls.loading",{},"Carregando câmeras…")}):null,Je?(0,a.jsx)("div",{className:"errorText",children:Je}):null,(0,a.jsx)("div",{className:"streamingFormGrid streamingFormGridCreatePrimary",style:{marginTop:10},children:(0,a.jsxs)("div",{className:"field",children:[(0,a.jsx)("label",{className:"label",children:t("ext.streaming.transmissions.camera_controls.camera",{},"Câmera")}),(0,a.jsx)("select",{className:"input",value:Ge,onChange:e=>Xe(String(e.target.value||"").trim()),disabled:0===Ye.length,children:Ye.map(e=>{const s=String(e.id||"").trim(),t=String(e.name||"").trim()||s;return(0,a.jsx)("option",{value:s,children:t},s)})}),0!==Ye.length||Ke?null:(0,a.jsx)("div",{className:"cardMeta",style:{marginTop:6},children:t("ext.streaming.transmissions.camera_controls.empty",{},"Nenhuma câmera encontrada. Cadastre uma câmera em Configurações > Câmeras.")})]})})]}):null,(0,a.jsx)("div",{className:"rowWrap",style:{marginTop:12,justifyContent:"flex-end"},children:(0,a.jsx)("button",{className:"primaryButton",type:"button",disabled:Ce,onClick:()=>{!async function(){ke(!0),Te(null);try{const e=_(Be);if(!vs.has(e))return void Te(t("ext.streaming.errors.invalid_host_server",{serverId:e},`Invalid host server: ${e}`));const s=Me.trim()||t("ext.streaming.transmissions.default_name",{},"Transmission"),a=d(Pe.trim()||s)||"stream";let i;if($e){const e=Ge.trim();if(!e)return void Te(t("ext.streaming.transmissions.camera_controls.select_camera_error",{},"Selecione uma câmera."));i={enabled:!0,camera_id:e}}const r=await(0,n.Fu)({name:s,path:a,enabled:!0,host_server_id:e,camera_controls:i??void 0,outputs:[{protocol:Ee,enabled:!0,resolution:{width:m(De,1280),height:m(We,720)},fps_limit:m(Oe,12)}]});V(e=>[r,...e.filter(e=>e.id!==r.id)]),Z(r.id),ne(g(r)),oe(!1),de(null),Se(!1),Ae(""),Le(""),Re("local"),qe(!1),Xe("")}catch(e){Te(e instanceof Error?e.message:String(e))}finally{ke(!1)}}()},children:Ce?t("ext.streaming.transmissions.creating",{},"Criando…"):t("ext.streaming.transmissions.create_button",{},"Criar transmissão")})})]}),(0,a.jsxs)(o.y,{open:te,title:t("ext.streaming.transmissions.discard_title",{},"Descartar alterações?"),closeAriaLabel:t("core.actions.close",{},"Close"),onClose:()=>{ae(!1),se(null)},children:[(0,a.jsx)("div",{className:"cardMeta",style:{marginBottom:10},children:t("ext.streaming.transmissions.discard_body",{},"Você tem alterações não salvas. Para trocar de transmissão, descarte ou salve primeiro.")}),(0,a.jsxs)("div",{className:"rowWrap",style:{justifyContent:"flex-end",gap:10},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>{ae(!1),se(null)},children:t("ext.streaming.common.cancel",{},"Cancelar")}),(0,a.jsx)("button",{className:"chipButton",type:"button",onClick:()=>{ee&&(oe(!1),de(null),ae(!1),Z(ee),se(null))},children:t("ext.streaming.transmissions.discard",{},"Descartar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",onClick:()=>{ae(!1),ws().then(()=>{ee&&(Z(ee),se(null))})},children:t("ext.streaming.transmissions.save",{},"Salvar")})]})]}),(0,a.jsxs)(o.y,{open:es,title:t("ext.streaming.transmissions.delete_title",{},"Excluir transmissão"),closeAriaLabel:t("core.actions.close",{},"Close"),onClose:()=>{ts||(ss(!1),ns(null))},children:[is?(0,a.jsx)("div",{className:"errorText",style:{marginBottom:10},children:is}):null,(0,a.jsx)("div",{className:"cardMeta",style:{marginBottom:10},children:t("ext.streaming.transmissions.delete_body",{},"Esta ação é irreversível.")}),(0,a.jsxs)("div",{className:"rowWrap",style:{justifyContent:"flex-end",gap:10},children:[(0,a.jsx)("button",{className:"chipButton",type:"button",disabled:ts,onClick:()=>ss(!1),children:t("ext.streaming.common.cancel",{},"Cancelar")}),(0,a.jsx)("button",{className:"primaryButton",type:"button",disabled:ts,onClick:()=>{!async function(){if(hs){as(!0),ns(null);try{await(0,n.yL)(hs.id),V(e=>e.filter(e=>e.id!==hs.id)),ue(e=>{const s={...e};return delete s[hs.id],s}),Z(null),ne(null),oe(!1),ss(!1)}catch(e){ns(e instanceof Error?e.message:String(e))}finally{as(!1)}}}()},children:ts?t("ext.streaming.transmissions.deleting",{},"Excluindo…"):t("ext.streaming.transmissions.delete",{},"Excluir")})]})]}),(0,a.jsx)(l.Y,{open:null!==rs,i18n:e,transmission:rs,engineRunning:Boolean(z?.running),processingServers:je,onClose:()=>os(null),onCreated:()=>{us()}})]});var Bs}},416(e,s,t){t.d(s,{J:()=>a});const a={en:{"ext.streaming.common.cancel":"Cancel","ext.streaming.settings.name":"Streams","ext.streaming.settings.desc":"Configure streams and generate playback URLs.","ext.streaming.settings.title":"Streams","ext.streaming.settings.subtitle":"Create streams, configure outputs (HLS/RTSP/WebRTC) and generate URLs via MediaMTX.","ext.streaming.settings.building":"Under construction.","ext.streaming.settings.quickstart":"Recommended flow","ext.streaming.settings.quickstart_step_1":"Create a stream with at least one output.","ext.streaming.settings.quickstart_step_2":"Adjust resolution/FPS/authentication per output.","ext.streaming.settings.quickstart_step_3":"Save, load URLs, then use the wizard to generate a pipeline.","ext.streaming.settings.health.loading":"Checking backend health…","ext.streaming.settings.health.ok":"Backend health: OK","ext.streaming.settings.health.failed":"Backend health: unavailable","ext.streaming.settings.health.refresh":"Refresh status","ext.streaming.settings.transmissions":"Configured streams","ext.streaming.engine.title":"Engine (MediaMTX)","ext.streaming.engine.loading":"Checking engine status…","ext.streaming.engine.running":"Engine status: running","ext.streaming.engine.stopped":"Engine status: stopped","ext.streaming.engine.start":"Start","ext.streaming.engine.starting":"Starting…","ext.streaming.engine.download":"Download engine","ext.streaming.engine.downloading":"Downloading…","ext.streaming.engine.stop":"Stop","ext.streaming.engine.stopping":"Stopping…","ext.streaming.engine.restart":"Restart","ext.streaming.engine.restarting":"Restarting…","ext.streaming.engine.reclaim":"Reclaim","ext.streaming.engine.reclaiming":"Reclaiming…","ext.streaming.engine.reclaim_confirm":"This will try to stop and cleanup stale MediaMTX processes for this data directory. Continue?","ext.streaming.engine.refresh":"Refresh","ext.streaming.engine.refreshing":"Refreshing…","ext.streaming.engine.test_rtsp":"RTSP (test)","ext.streaming.engine.test_hls":"HLS (test)","ext.streaming.engine.test_webrtc":"WebRTC/WHEP (test)","ext.streaming.engine.settings_title":"Engine settings","ext.streaming.engine.settings_loading":"Loading…","ext.streaming.engine.settings_discard":"Discard","ext.streaming.engine.settings_apply":"Apply","ext.streaming.engine.settings_applying":"Applying…","ext.streaming.engine.expose_to_lan":"Expose on LAN (0.0.0.0)","ext.streaming.engine.port_rtsp":"RTSP port","ext.streaming.engine.port_hls":"HLS port","ext.streaming.engine.port_webrtc":"WebRTC port","ext.streaming.engine.port_api":"API port","ext.streaming.engine.ice_servers_label":"STUN/TURN servers (optional, one per line)","ext.streaming.engine.ice_servers_hint":"Only needed to traverse NAT. On a simple LAN, leave it empty.","ext.streaming.engine.meta_version":"Version","ext.streaming.engine.meta_pid":"PID","ext.streaming.engine.meta_uptime":"Uptime","ext.streaming.engine.meta_api":"API","ext.streaming.engine.meta_restarts":"Restarts","ext.streaming.engine.meta_binary":"Binary","ext.streaming.engine.meta_config":"Config","ext.streaming.engine.meta_log":"Log","ext.streaming.engine.orphan_processes":"Found {{count}} external MediaMTX process(es) for this data directory: {{pids}}.","ext.streaming.processing_servers.loading":"Loading processing servers…","ext.streaming.processing_servers.local_label":"local (this machine)","ext.streaming.errors.invalid_host_server":"Invalid host server: {{serverId}}","ext.streaming.transmissions.search":"Search streams…","ext.streaming.transmissions.loading":"Loading…","ext.streaming.transmissions.empty":"No streams created yet.","ext.streaming.transmissions.add":"Add stream","ext.streaming.transmissions.create":"Create stream","ext.streaming.transmissions.create_button":"Create stream","ext.streaming.transmissions.creating":"Creating…","ext.streaming.transmissions.select":"Select a stream to edit.","ext.streaming.transmissions.select_hint":"Tip: create a stream, then use the wizard to generate a pipeline.","ext.streaming.transmissions.meta_line":"Host: {{host}} • Path: {{path}} • Outputs: {{outputs}}","ext.streaming.transmissions.badge_disabled":"off","ext.streaming.transmissions.badge_disabled_title":"Disabled","ext.streaming.transmissions.badge_unsaved":"pending","ext.streaming.transmissions.badge_unsaved_title":"Unsaved changes","ext.streaming.transmissions.discard":"Discard","ext.streaming.transmissions.save":"Save","ext.streaming.transmissions.saving":"Saving…","ext.streaming.transmissions.delete":"Delete","ext.streaming.transmissions.discard_title":"Discard changes?","ext.streaming.transmissions.discard_body":"You have unsaved changes. Discard or save before switching streams.","ext.streaming.transmissions.delete_title":"Delete stream","ext.streaming.transmissions.delete_body":"This action cannot be undone.","ext.streaming.transmissions.deleting":"Deleting…","ext.streaming.transmissions.basic":"Basics","ext.streaming.transmissions.name":"Name","ext.streaming.transmissions.default_name":"Stream","ext.streaming.transmissions.path":"Path/slug","ext.streaming.transmissions.path_hint":"Use only a-z, 0-9, - and _. The server normalizes automatically.","ext.streaming.transmissions.enabled":"Enabled","ext.streaming.transmissions.host_server":"Host server","ext.streaming.transmissions.host_server_hint":"This stream will be hosted on this processing server.","ext.streaming.transmissions.host_server_missing":"This host server no longer exists. Select another one before saving.","ext.streaming.transmissions.placeholder":"Placeholder","ext.streaming.transmissions.placeholder.gray":"Gray background","ext.streaming.transmissions.placeholder.black":"Black background","ext.streaming.transmissions.arbitration":"Arbitration","ext.streaming.transmissions.arbitration.priority_latest":"Priority, then latest","ext.streaming.transmissions.arbitration.latest":"Latest writer wins","ext.streaming.transmissions.outputs":"Outputs","ext.streaming.transmissions.outputs_hint":"Each output can have different resolution/FPS/bitrate.","ext.streaming.transmissions.outputs_empty":"No outputs added yet.","ext.streaming.transmissions.protocol":"Protocol","ext.streaming.transmissions.width":"Width","ext.streaming.transmissions.height":"Height","ext.streaming.transmissions.fps":"FPS","ext.streaming.transmissions.camera_controls.title":"Camera controls","ext.streaming.transmissions.camera_controls.enable":"Enable camera controls","ext.streaming.transmissions.camera_controls.hint":"When enabled, this stream can control a camera (presets/PTZ) via API.","ext.streaming.transmissions.camera_controls.loading":"Loading cameras…","ext.streaming.transmissions.camera_controls.camera":"Camera","ext.streaming.transmissions.camera_controls.empty":"No cameras found. Add a camera in Settings > Cameras.","ext.streaming.transmissions.camera_controls.select_camera_error":"Select a camera.","ext.streaming.transmissions.urls":"URLs","ext.streaming.transmissions.urls_hint":"URLs are generated by the engine (MediaMTX).","ext.streaming.transmissions.load_urls":"Load URLs","ext.streaming.transmissions.loading_urls":"Loading URLs…","ext.streaming.transmissions.urls_empty":"Load URLs to view them here.","ext.streaming.transmissions.urls_save_warning":"Save changes to ensure paths/outputs are up to date before sharing URLs.","ext.streaming.transmissions.engine_off_warning":"The engine is stopped: URLs exist, but playback will not work until you start it.","ext.streaming.transmissions.engine_path":"Engine path","ext.streaming.transmissions.auth_required":"Requires authentication.","ext.streaming.transmissions.auth_required_user":"Requires authentication (username: {{username}}).","ext.streaming.transmissions.auth_not_required":"No authentication.","ext.streaming.transmissions.copy_url":"Copy URL","ext.streaming.transmissions.copied":"Copied!","ext.streaming.outputs.add_hls":"+ HLS","ext.streaming.outputs.add_rtsp":"+ RTSP","ext.streaming.outputs.add_webrtc":"+ WebRTC","ext.streaming.outputs.remove":"Remove","ext.streaming.outputs.output_id":"Output ID","ext.streaming.outputs.enabled":"Enabled","ext.streaming.outputs.protocol":"Protocol","ext.streaming.outputs.width":"Width","ext.streaming.outputs.height":"Height","ext.streaming.outputs.fps":"FPS","ext.streaming.outputs.bitrate":"Bitrate (kbps)","ext.streaming.outputs.latency":"Latency","ext.streaming.outputs.latency_option.normal":"Normal","ext.streaming.outputs.latency_option.low":"Low","ext.streaming.outputs.latency_option.ultra_low":"Ultra low","ext.streaming.outputs.auth":"Authentication (optional)","ext.streaming.outputs.auth_enabled":"Enable username/password","ext.streaming.outputs.username":"Username","ext.streaming.outputs.password":"Password","ext.streaming.outputs.auth_note":"Credentials are applied in MediaMTX for playback of this output.","ext.streaming.wizard.open":"Create pipeline for this stream","ext.streaming.wizard.title":"Create pipeline","ext.streaming.wizard.engine_warning":"The streaming engine is stopped. You can create the pipeline now and start the engine later.","ext.streaming.wizard.loading_cameras":"Loading cameras…","ext.streaming.wizard.camera":"Camera","ext.streaming.wizard.camera_placeholder":"Select a camera","ext.streaming.wizard.camera_search":"Search by name or ID","ext.streaming.wizard.camera_no_results":"No cameras match your search.","ext.streaming.wizard.camera_empty":"No cameras found. Add a camera in Settings > Cameras.","ext.streaming.wizard.camera_unnamed":"Unnamed camera","ext.streaming.wizard.preset":"Preset","ext.streaming.wizard.presets.simple_stream.title":"Simple stream","ext.streaming.wizard.presets.simple_stream.desc":"camera.source + optional fps reducer + stream.publish_video","ext.streaming.wizard.presets.motion_gate_stream.title":"Motion gate stream","ext.streaming.wizard.presets.motion_gate_stream.desc":"camera.source + motion gate + fps reducer + stream.publish_video","ext.streaming.wizard.presets.detection_stream.title":"Detection stream","ext.streaming.wizard.presets.detection_stream.desc":"camera.source + object detection + stream.publish_video","ext.streaming.wizard.presets.tracking_stream.title":"Tracking stream","ext.streaming.wizard.presets.tracking_stream.desc":"camera.source + object tracking + stream.publish_video","ext.streaming.wizard.presets.segmentation_stream.title":"Segmentation stream","ext.streaming.wizard.presets.segmentation_stream.desc":"camera.source + object segmentation + stream.publish_video","ext.streaming.wizard.pipeline_name":"Pipeline name (optional)","ext.streaming.wizard.processing_server":"Processing server","ext.streaming.wizard.host_mismatch_inline":"Stream host: {{transmissionHost}}. Pipeline host: {{pipelineHost}}. They must match.","ext.streaming.wizard.fps":"FPS limit","ext.streaming.wizard.writer_priority":"Writer priority","ext.streaming.wizard.source_backend":"Camera backend","ext.streaming.wizard.source_backend.option.auto":"Auto","ext.streaming.wizard.source_backend.option.opencv":"OpenCV","ext.streaming.wizard.source_backend.option.ffmpeg":"FFmpeg","ext.streaming.wizard.bypass_mode":"Bypass mode","ext.streaming.wizard.bypass_mode.option.auto":"Auto","ext.streaming.wizard.bypass_mode.option.force_on":"Force on","ext.streaming.wizard.bypass_mode.option.force_off":"Force off","ext.streaming.wizard.resize_mode":"Resize mode","ext.streaming.wizard.resize_mode.option.contain":"Contain","ext.streaming.wizard.resize_mode.option.none":"No resize","ext.streaming.wizard.motion_sensitivity":"Motion sensitivity","ext.streaming.wizard.motion_hold":"Motion hold (s)","ext.streaming.wizard.yolo_conf":"Vision confidence","ext.streaming.wizard.yolo_filter_enabled":"Filter frames after vision (recommended)","ext.streaming.wizard.yolo_filter_enabled.hint":"When disabled, the pipeline still runs vision inference but keeps all frames.","ext.streaming.wizard.detection_categories":"Detection categories (csv)","ext.streaming.wizard.tracking_categories":"Tracking categories (csv)","ext.streaming.wizard.enabled":"Enable pipeline after creation","ext.streaming.wizard.use_fps_reducer":"Add core.fps_reducer step","ext.streaming.wizard.create":"Create pipeline","ext.streaming.wizard.creating":"Creating…","ext.streaming.wizard.done_title":"Pipeline created","ext.streaming.wizard.pipeline_created_name":"Pipeline","ext.streaming.wizard.open_pipelines":"Open Pipelines","ext.streaming.wizard.errors.select_camera":"Select a camera.","ext.streaming.wizard.errors.invalid_processing_server":"Invalid processing server: {{serverId}}","ext.streaming.wizard.errors.host_mismatch":"Stream host is {{transmissionHost}}. Select the same processing server for the pipeline.","ext.streaming.runtime.loading":"Refreshing demand and publisher status…","ext.streaming.runtime.demand_on":"demand:on","ext.streaming.runtime.demand_off":"demand:off","ext.streaming.runtime.publisher_running":"running","ext.streaming.runtime.publisher_stopped":"stopped","ext.streaming.runtime.output_status":"Viewers/publisher"},"pt-BR":{"ext.streaming.common.cancel":"Cancelar","ext.streaming.settings.name":"Transmissões","ext.streaming.settings.desc":"Configure transmissões e gere URLs de reprodução.","ext.streaming.settings.title":"Transmissões","ext.streaming.settings.subtitle":"Crie transmissões, configure saídas (HLS/RTSP/WebRTC) e gere URLs via MediaMTX.","ext.streaming.settings.building":"Em construção.","ext.streaming.settings.quickstart":"Fluxo recomendado","ext.streaming.settings.quickstart_step_1":"Crie uma transmissão com pelo menos uma saída.","ext.streaming.settings.quickstart_step_2":"Ajuste resolução/FPS/autenticação por saída.","ext.streaming.settings.quickstart_step_3":"Salve, carregue URLs e use o wizard para gerar um fluxo.","ext.streaming.settings.health.loading":"Verificando saúde do backend…","ext.streaming.settings.health.ok":"Saúde do backend: OK","ext.streaming.settings.health.failed":"Saúde do backend: indisponível","ext.streaming.settings.health.refresh":"Atualizar status","ext.streaming.settings.transmissions":"Transmissões configuradas","ext.streaming.engine.title":"Engine (MediaMTX)","ext.streaming.engine.loading":"Verificando status da engine…","ext.streaming.engine.running":"Status da engine: rodando","ext.streaming.engine.stopped":"Status da engine: parada","ext.streaming.engine.start":"Iniciar","ext.streaming.engine.starting":"Iniciando…","ext.streaming.engine.download":"Baixar engine","ext.streaming.engine.downloading":"Baixando…","ext.streaming.engine.stop":"Parar","ext.streaming.engine.stopping":"Parando…","ext.streaming.engine.restart":"Reiniciar","ext.streaming.engine.restarting":"Reiniciando…","ext.streaming.engine.reclaim":"Recuperar","ext.streaming.engine.reclaiming":"Recuperando…","ext.streaming.engine.reclaim_confirm":"Isso vai tentar parar e limpar processos MediaMTX antigos deste diretório de dados. Continuar?","ext.streaming.engine.refresh":"Atualizar","ext.streaming.engine.refreshing":"Atualizando…","ext.streaming.engine.test_rtsp":"RTSP (teste)","ext.streaming.engine.test_hls":"HLS (teste)","ext.streaming.engine.test_webrtc":"WebRTC/WHEP (teste)","ext.streaming.engine.settings_title":"Configuração da engine","ext.streaming.engine.settings_loading":"Carregando…","ext.streaming.engine.settings_discard":"Descartar","ext.streaming.engine.settings_apply":"Aplicar","ext.streaming.engine.settings_applying":"Aplicando…","ext.streaming.engine.expose_to_lan":"Expor na LAN (0.0.0.0)","ext.streaming.engine.port_rtsp":"Porta RTSP","ext.streaming.engine.port_hls":"Porta HLS","ext.streaming.engine.port_webrtc":"Porta WebRTC","ext.streaming.engine.port_api":"Porta API","ext.streaming.engine.ice_servers_label":"Servidores STUN/TURN (opcional, um por linha)","ext.streaming.engine.ice_servers_hint":"Use apenas se precisar atravessar NAT. Em uma LAN simples, deixe vazio.","ext.streaming.engine.meta_version":"Versão","ext.streaming.engine.meta_pid":"PID","ext.streaming.engine.meta_uptime":"Uptime","ext.streaming.engine.meta_api":"API","ext.streaming.engine.meta_restarts":"Reinícios","ext.streaming.engine.meta_binary":"Binário","ext.streaming.engine.meta_config":"Config","ext.streaming.engine.meta_log":"Log","ext.streaming.engine.orphan_processes":"Foram encontrados {{count}} processos externos do MediaMTX para este diretório de dados: {{pids}}.","ext.streaming.processing_servers.loading":"Carregando servidores de processamento…","ext.streaming.processing_servers.local_label":"local (esta máquina)","ext.streaming.errors.invalid_host_server":"Servidor host inválido: {{serverId}}","ext.streaming.transmissions.search":"Buscar transmissões…","ext.streaming.transmissions.loading":"Carregando…","ext.streaming.transmissions.empty":"Nenhuma transmissão criada ainda.","ext.streaming.transmissions.add":"Adicionar transmissão","ext.streaming.transmissions.create":"Criar transmissão","ext.streaming.transmissions.create_button":"Criar transmissão","ext.streaming.transmissions.creating":"Criando…","ext.streaming.transmissions.select":"Selecione uma transmissão para editar.","ext.streaming.transmissions.select_hint":"Dica: crie uma transmissão e depois use o wizard para gerar um fluxo.","ext.streaming.transmissions.meta_line":"Host: {{host}} • Path: {{path}} • Saídas: {{outputs}}","ext.streaming.transmissions.badge_disabled":"off","ext.streaming.transmissions.badge_disabled_title":"Desativada","ext.streaming.transmissions.badge_unsaved":"pendente","ext.streaming.transmissions.badge_unsaved_title":"Alterações não salvas","ext.streaming.transmissions.discard":"Descartar","ext.streaming.transmissions.save":"Salvar","ext.streaming.transmissions.saving":"Salvando…","ext.streaming.transmissions.delete":"Excluir","ext.streaming.transmissions.discard_title":"Descartar alterações?","ext.streaming.transmissions.discard_body":"Você tem alterações não salvas. Descarte ou salve antes de trocar de transmissão.","ext.streaming.transmissions.delete_title":"Excluir transmissão","ext.streaming.transmissions.delete_body":"Esta ação não pode ser desfeita.","ext.streaming.transmissions.deleting":"Excluindo…","ext.streaming.transmissions.basic":"Básico","ext.streaming.transmissions.name":"Nome","ext.streaming.transmissions.default_name":"Transmissão","ext.streaming.transmissions.path":"Path/slug","ext.streaming.transmissions.path_hint":"Use apenas a-z, 0-9, - e _. O servidor normaliza automaticamente.","ext.streaming.transmissions.enabled":"Ativa","ext.streaming.transmissions.host_server":"Host da transmissão","ext.streaming.transmissions.host_server_hint":"A transmissão será hospedada neste servidor de processamento.","ext.streaming.transmissions.host_server_missing":"Este host server não existe mais. Selecione outro antes de salvar.","ext.streaming.transmissions.placeholder":"Placeholder","ext.streaming.transmissions.placeholder.gray":"Fundo cinza","ext.streaming.transmissions.placeholder.black":"Fundo preto","ext.streaming.transmissions.arbitration":"Arbitragem","ext.streaming.transmissions.arbitration.priority_latest":"Prioridade, depois mais recente","ext.streaming.transmissions.arbitration.latest":"Mais recente vence","ext.streaming.transmissions.outputs":"Saídas","ext.streaming.transmissions.outputs_hint":"Cada saída pode ter resolução/FPS/bitrate diferentes.","ext.streaming.transmissions.outputs_empty":"Nenhuma saída adicionada ainda.","ext.streaming.transmissions.protocol":"Protocolo","ext.streaming.transmissions.width":"Largura","ext.streaming.transmissions.height":"Altura","ext.streaming.transmissions.fps":"FPS","ext.streaming.transmissions.camera_controls.title":"Controles de câmera","ext.streaming.transmissions.camera_controls.enable":"Habilitar controles de câmera","ext.streaming.transmissions.camera_controls.hint":"Quando habilitado, esta transmissão poderá controlar uma câmera (presets/PTZ) via API.","ext.streaming.transmissions.camera_controls.loading":"Carregando câmeras…","ext.streaming.transmissions.camera_controls.camera":"Câmera","ext.streaming.transmissions.camera_controls.empty":"Nenhuma câmera encontrada. Cadastre uma câmera em Configurações > Câmeras.","ext.streaming.transmissions.camera_controls.select_camera_error":"Selecione uma câmera.","ext.streaming.transmissions.urls":"URLs","ext.streaming.transmissions.urls_hint":"As URLs são geradas pela engine (MediaMTX).","ext.streaming.transmissions.load_urls":"Carregar URLs","ext.streaming.transmissions.loading_urls":"Carregando URLs…","ext.streaming.transmissions.urls_empty":"Carregue as URLs para visualizar aqui.","ext.streaming.transmissions.urls_save_warning":"Salve alterações para garantir que paths/saídas estejam atualizados antes de compartilhar URLs.","ext.streaming.transmissions.engine_off_warning":"A engine está parada: as URLs existem, mas o playback não funcionará até iniciar.","ext.streaming.transmissions.engine_path":"Path da engine","ext.streaming.transmissions.auth_required":"Requer autenticação.","ext.streaming.transmissions.auth_required_user":"Requer autenticação (usuário: {{username}}).","ext.streaming.transmissions.auth_not_required":"Sem autenticação.","ext.streaming.transmissions.copy_url":"Copiar URL","ext.streaming.transmissions.copied":"Copiado!","ext.streaming.outputs.add_hls":"+ HLS","ext.streaming.outputs.add_rtsp":"+ RTSP","ext.streaming.outputs.add_webrtc":"+ WebRTC","ext.streaming.outputs.remove":"Remover","ext.streaming.outputs.output_id":"ID da saída","ext.streaming.outputs.enabled":"Ativa","ext.streaming.outputs.protocol":"Protocolo","ext.streaming.outputs.width":"Largura","ext.streaming.outputs.height":"Altura","ext.streaming.outputs.fps":"FPS","ext.streaming.outputs.bitrate":"Bitrate (kbps)","ext.streaming.outputs.latency":"Latência","ext.streaming.outputs.latency_option.normal":"Normal","ext.streaming.outputs.latency_option.low":"Baixa","ext.streaming.outputs.latency_option.ultra_low":"Ultra baixa","ext.streaming.outputs.auth":"Autenticação (opcional)","ext.streaming.outputs.auth_enabled":"Habilitar usuário/senha","ext.streaming.outputs.username":"Usuário","ext.streaming.outputs.password":"Senha","ext.streaming.outputs.auth_note":"As credenciais são aplicadas no MediaMTX para leitura/playback desta saída.","ext.streaming.wizard.open":"Criar fluxo com esta transmissão","ext.streaming.wizard.title":"Criar fluxo","ext.streaming.wizard.engine_warning":"A engine de streaming está parada. Você pode criar o fluxo agora e iniciar a engine depois.","ext.streaming.wizard.loading_cameras":"Carregando câmeras…","ext.streaming.wizard.camera":"Câmera","ext.streaming.wizard.camera_placeholder":"Selecione uma câmera","ext.streaming.wizard.camera_search":"Buscar por nome ou ID","ext.streaming.wizard.camera_no_results":"Nenhuma câmera corresponde à busca.","ext.streaming.wizard.camera_empty":"Nenhuma câmera encontrada. Cadastre uma câmera em Configurações > Câmeras.","ext.streaming.wizard.camera_unnamed":"Câmera sem nome","ext.streaming.wizard.preset":"Preset","ext.streaming.wizard.presets.simple_stream.title":"Stream simples","ext.streaming.wizard.presets.simple_stream.desc":"camera.source + redutor de FPS (opcional) + stream.publish_video","ext.streaming.wizard.presets.motion_gate_stream.title":"Stream com gate de movimento","ext.streaming.wizard.presets.motion_gate_stream.desc":"camera.source + gate de movimento + redutor de FPS + stream.publish_video","ext.streaming.wizard.presets.detection_stream.title":"Stream com detecção","ext.streaming.wizard.presets.detection_stream.desc":"camera.source + detecção de objetos + stream.publish_video","ext.streaming.wizard.presets.tracking_stream.title":"Stream com tracking","ext.streaming.wizard.presets.tracking_stream.desc":"camera.source + tracking de objetos + stream.publish_video","ext.streaming.wizard.presets.segmentation_stream.title":"Stream com segmentação","ext.streaming.wizard.presets.segmentation_stream.desc":"camera.source + segmentação de objetos + stream.publish_video","ext.streaming.wizard.pipeline_name":"Nome do fluxo (opcional)","ext.streaming.wizard.processing_server":"Servidor de processamento","ext.streaming.wizard.host_mismatch_inline":"Host da transmissão: {{transmissionHost}}. Host do fluxo: {{pipelineHost}}. Eles precisam ser iguais.","ext.streaming.wizard.fps":"Limite de FPS","ext.streaming.wizard.writer_priority":"Prioridade do writer","ext.streaming.wizard.source_backend":"Backend da câmera","ext.streaming.wizard.source_backend.option.auto":"Auto","ext.streaming.wizard.source_backend.option.opencv":"OpenCV","ext.streaming.wizard.source_backend.option.ffmpeg":"FFmpeg","ext.streaming.wizard.bypass_mode":"Modo bypass","ext.streaming.wizard.bypass_mode.option.auto":"Auto","ext.streaming.wizard.bypass_mode.option.force_on":"Forçar ligado","ext.streaming.wizard.bypass_mode.option.force_off":"Forçar desligado","ext.streaming.wizard.resize_mode":"Modo de redimensionamento","ext.streaming.wizard.resize_mode.option.contain":"Contain","ext.streaming.wizard.resize_mode.option.none":"Sem resize","ext.streaming.wizard.motion_sensitivity":"Sensibilidade de movimento","ext.streaming.wizard.motion_hold":"Hold de movimento (s)","ext.streaming.wizard.yolo_conf":"Confiança da visão","ext.streaming.wizard.yolo_filter_enabled":"Filtrar frames após visão (recomendado)","ext.streaming.wizard.yolo_filter_enabled.hint":"Quando desligado, o fluxo ainda executa a inferência de visão, mas mantém todos os frames.","ext.streaming.wizard.detection_categories":"Categorias de detecção (csv)","ext.streaming.wizard.tracking_categories":"Categorias de tracking (csv)","ext.streaming.wizard.enabled":"Habilitar fluxo após criação","ext.streaming.wizard.use_fps_reducer":"Inserir step core.fps_reducer","ext.streaming.wizard.create":"Criar fluxo","ext.streaming.wizard.creating":"Criando…","ext.streaming.wizard.done_title":"Fluxo criado","ext.streaming.wizard.pipeline_created_name":"Fluxo","ext.streaming.wizard.open_pipelines":"Abrir Fluxos","ext.streaming.wizard.errors.select_camera":"Selecione uma câmera.","ext.streaming.wizard.errors.invalid_processing_server":"Servidor de processamento inválido: {{serverId}}","ext.streaming.wizard.errors.host_mismatch":"O host da transmissão é {{transmissionHost}}. Selecione o mesmo servidor de processamento para o fluxo.","ext.streaming.runtime.loading":"Atualizando demanda e status do publisher…","ext.streaming.runtime.demand_on":"demanda:on","ext.streaming.runtime.demand_off":"demanda:off","ext.streaming.runtime.publisher_running":"rodando","ext.streaming.runtime.publisher_stopped":"parado","ext.streaming.runtime.output_status":"Viewers/publisher"}}},697(e,s,t){t.d(s,{y:()=>n});var a=t(870),i=(t(496),t(428));function n({title:e,open:s,onClose:t,closeAriaLabel:n,children:r,panelStyle:o}){if(!s)return null;const l=String(n||"").trim()||"Close";return(0,i.createPortal)((0,a.jsx)("div",{className:"modalBackdrop",style:{zIndex:"calc(var(--z-modal) + 1)"},onMouseDown:e=>{e.target===e.currentTarget&&t()},role:"presentation",children:(0,a.jsxs)("div",{className:"modalPanel",style:{width:"min(920px, calc(100vw - 28px))",...o??{}},role:"dialog","aria-modal":"true","aria-label":e,children:[(0,a.jsxs)("div",{className:"modalHeader",children:[(0,a.jsx)("div",{className:"modalTitle",children:e}),(0,a.jsx)("button",{className:"iconButton",type:"button",onClick:t,"aria-label":l,children:(0,a.jsx)("i",{className:"fa-solid fa-xmark","aria-hidden":"true"})})]}),(0,a.jsx)("div",{className:"modalBody",children:r})]})}),document.body)}},703(e,s,t){t.r(s),t.d(s,{activate:()=>n});var a=t(323),i=t(416);function n(e){e.i18n.registerTranslations(i.J),e.registerSettingsPanel((0,a.w)())}},870(e,s,t){e.exports=t(94)},901(e,s,t){async function a(e){const s=`HTTP ${e.status}`;try{const a=await e.json();if(t=a,!Boolean(t)||"object"!=typeof t)return s;const i=a.detail;return"string"==typeof i&&i.trim()?i.trim():s}catch{try{return(await e.text()).trim()||s}catch{return s}}var t}async function i(e,s){const t=await fetch(e,s);if(!t.ok)throw new Error(await a(t));return await t.json()}async function n(e){return i("/api/streams/health",{signal:e})}async function r(e){return i("/api/streams/engine/status",{signal:e})}async function o(e){return i(`/api/streams/engine/${e}`,{method:"POST"})}async function l(){return i("/api/streams/engine/download",{method:"POST"})}async function m(e){return i("/api/streams/settings",{signal:e})}async function c(e){return i("/api/streams/settings",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(e??{})})}async function d(e){return i("/api/streams/transmissions",{signal:e})}async function g(e){return i("/api/streams/transmissions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}async function u(e,s){return i(`/api/streams/transmissions/${encodeURIComponent(e)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}async function p(e){const s=await fetch(`/api/streams/transmissions/${encodeURIComponent(e)}`,{method:"DELETE"});if(!s.ok)throw new Error(await a(s))}async function x(e){return i(`/api/streams/transmissions/${encodeURIComponent(e)}/urls`)}async function h(e){return i("/api/cameras/index",{signal:e})}async function _(e){const s=await i("/api/processing-servers",{signal:e});return Array.isArray(s.servers)?s.servers:[]}async function v(e){return i("/api/streams/wizard/create-pipeline",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}t.d(s,{A$:()=>v,Cs:()=>u,Fu:()=>g,I$:()=>d,JB:()=>r,WO:()=>m,bO:()=>h,cr:()=>_,eR:()=>c,hF:()=>o,jP:()=>l,m9:()=>n,pU:()=>x,yL:()=>p})}}]);
|
|
@@ -21,6 +21,28 @@ from .mediamtx_processes import kill_mediamtx_processes_for_config_path
|
|
|
21
21
|
from .platform import detect_mediamtx_platform
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def _split_env_list(name: str) -> list[str]:
|
|
25
|
+
out: list[str] = []
|
|
26
|
+
seen: set[str] = set()
|
|
27
|
+
for item in str(os.getenv(name) or "").replace(";", ",").split(","):
|
|
28
|
+
value = item.strip()
|
|
29
|
+
if not value:
|
|
30
|
+
continue
|
|
31
|
+
key = value.lower()
|
|
32
|
+
if key in seen:
|
|
33
|
+
continue
|
|
34
|
+
seen.add(key)
|
|
35
|
+
out.append(value)
|
|
36
|
+
return out
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _env_address(name: str, *, default: str) -> str:
|
|
40
|
+
raw = os.getenv(name)
|
|
41
|
+
if raw is None:
|
|
42
|
+
return default
|
|
43
|
+
return str(raw).strip()
|
|
44
|
+
|
|
45
|
+
|
|
24
46
|
@dataclass(frozen=True, slots=True)
|
|
25
47
|
class MediaMtxPorts:
|
|
26
48
|
rtsp: int
|
|
@@ -494,12 +516,20 @@ class MediaMtxEngineManager:
|
|
|
494
516
|
paths=list(self._engine_paths),
|
|
495
517
|
enable_webrtc=True,
|
|
496
518
|
webrtc_ice_servers=list(getattr(engine_settings, "webrtc_ice_servers", []) or []),
|
|
519
|
+
webrtc_additional_hosts=_split_env_list("TOPOSYNC_STREAMING_WEBRTC_ADDITIONAL_HOSTS"),
|
|
497
520
|
path_auth=path_auth_entries,
|
|
498
521
|
path_configs=dict(self._path_configs_by_path),
|
|
499
522
|
api_allow_origins=["*"],
|
|
500
523
|
hls_allow_origins=["*"],
|
|
501
524
|
webrtc_allow_origins=["*"],
|
|
502
|
-
webrtc_local_udp_address=
|
|
525
|
+
webrtc_local_udp_address=_env_address(
|
|
526
|
+
"TOPOSYNC_STREAMING_WEBRTC_LOCAL_UDP_ADDRESS",
|
|
527
|
+
default=":0",
|
|
528
|
+
),
|
|
529
|
+
webrtc_local_tcp_address=_env_address(
|
|
530
|
+
"TOPOSYNC_STREAMING_WEBRTC_LOCAL_TCP_ADDRESS",
|
|
531
|
+
default="",
|
|
532
|
+
),
|
|
503
533
|
)
|
|
504
534
|
|
|
505
535
|
config_hash = hashlib.sha256(config_text.encode("utf-8")).hexdigest()
|
|
@@ -77,6 +77,7 @@ def render_mediamtx_config(
|
|
|
77
77
|
paths: list[str],
|
|
78
78
|
enable_webrtc: bool = False,
|
|
79
79
|
webrtc_ice_servers: list[str] | None = None,
|
|
80
|
+
webrtc_additional_hosts: list[str] | None = None,
|
|
80
81
|
path_auth: list[MediaMTXPathAuth] | None = None,
|
|
81
82
|
path_configs: dict[str, dict[str, object]] | None = None,
|
|
82
83
|
api_allow_origins: list[str] | None = None,
|
|
@@ -201,6 +202,13 @@ def render_mediamtx_config(
|
|
|
201
202
|
if enable_webrtc and ports.webrtc is not None:
|
|
202
203
|
lines.append(f"webrtcAddress: {_address(bind_host, ports.webrtc)}")
|
|
203
204
|
lines.append(f"webrtcAllowOrigins: {_as_yaml_list(default_webrtc_origins)}")
|
|
205
|
+
normalized_additional_hosts = [
|
|
206
|
+
str(item or "").strip()
|
|
207
|
+
for item in (webrtc_additional_hosts or [])
|
|
208
|
+
if str(item or "").strip()
|
|
209
|
+
]
|
|
210
|
+
if normalized_additional_hosts:
|
|
211
|
+
lines.append(f"webrtcAdditionalHosts: {_as_yaml_list(normalized_additional_hosts)}")
|
|
204
212
|
lines.append(f"webrtcLocalUDPAddress: {_yaml_single_quote(str(webrtc_local_udp_address or ':0'))}")
|
|
205
213
|
local_tcp = str(webrtc_local_tcp_address or "").strip()
|
|
206
214
|
if local_tcp:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import unicodedata
|
|
3
4
|
from typing import Any, Literal
|
|
4
5
|
|
|
5
6
|
from toposync.runtime.pipelines.templates import safe_pipeline_name
|
|
@@ -20,14 +21,42 @@ STREAMING_WIZARD_PRESETS: tuple[WizardPresetId, ...] = (
|
|
|
20
21
|
"segmentation_stream",
|
|
21
22
|
)
|
|
22
23
|
|
|
24
|
+
DEFAULT_STREAMING_DETECTION_MODEL_ID = "rfdetr_det_medium"
|
|
25
|
+
|
|
26
|
+
_GENERIC_NAME_COMPONENTS = {"stream", "transmission", "transmissao", "fluxo"}
|
|
27
|
+
_PRESET_NAME_COMPONENTS: dict[str, str] = {
|
|
28
|
+
"simple_stream": "stream",
|
|
29
|
+
"motion_gate_stream": "motion",
|
|
30
|
+
"detection_stream": "detection",
|
|
31
|
+
"tracking_stream": "tracking",
|
|
32
|
+
"segmentation_stream": "segmentation",
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
|
|
24
36
|
def suggested_streaming_wizard_pipeline_name(
|
|
25
37
|
*,
|
|
26
38
|
transmission_id: str,
|
|
27
39
|
camera_id: str,
|
|
28
40
|
preset_id: WizardPresetId,
|
|
41
|
+
transmission_name: str | None = None,
|
|
42
|
+
transmission_path: str | None = None,
|
|
43
|
+
camera_name: str | None = None,
|
|
29
44
|
) -> str:
|
|
30
|
-
|
|
45
|
+
transmission_component = _pick_name_component(
|
|
46
|
+
transmission_path,
|
|
47
|
+
transmission_name,
|
|
48
|
+
transmission_id,
|
|
49
|
+
fallback="stream",
|
|
50
|
+
skip_generic=True,
|
|
51
|
+
)
|
|
52
|
+
camera_component = _pick_name_component(camera_id, camera_name)
|
|
53
|
+
preset_component = _PRESET_NAME_COMPONENTS.get(str(preset_id), "stream")
|
|
54
|
+
|
|
55
|
+
components = [transmission_component, camera_component, preset_component]
|
|
56
|
+
if camera_component and _is_generic_component(transmission_component):
|
|
57
|
+
components = [camera_component, preset_component]
|
|
58
|
+
|
|
59
|
+
base = "__".join(_dedupe_name_components(components))
|
|
31
60
|
return safe_pipeline_name(base)
|
|
32
61
|
|
|
33
62
|
|
|
@@ -114,7 +143,7 @@ def build_streaming_wizard_graph(
|
|
|
114
143
|
"id": "detect",
|
|
115
144
|
"operator": "vision.detect",
|
|
116
145
|
"config": {
|
|
117
|
-
"model_id":
|
|
146
|
+
"model_id": DEFAULT_STREAMING_DETECTION_MODEL_ID,
|
|
118
147
|
"categories": detection_categories,
|
|
119
148
|
"confidence_threshold": float(yolo_confidence),
|
|
120
149
|
"emit_mode": yolo_emit_mode,
|
|
@@ -130,7 +159,7 @@ def build_streaming_wizard_graph(
|
|
|
130
159
|
"id": "detect",
|
|
131
160
|
"operator": "vision.detect",
|
|
132
161
|
"config": {
|
|
133
|
-
"model_id":
|
|
162
|
+
"model_id": DEFAULT_STREAMING_DETECTION_MODEL_ID,
|
|
134
163
|
"categories": tracking_categories,
|
|
135
164
|
"confidence_threshold": float(yolo_confidence),
|
|
136
165
|
"emit_mode": "annotate",
|
|
@@ -203,6 +232,69 @@ def _append_edge(
|
|
|
203
232
|
)
|
|
204
233
|
|
|
205
234
|
|
|
235
|
+
def _pick_name_component(
|
|
236
|
+
*values: str | None,
|
|
237
|
+
fallback: str = "",
|
|
238
|
+
skip_generic: bool = False,
|
|
239
|
+
) -> str:
|
|
240
|
+
candidates = [_safe_name_component(value) for value in values]
|
|
241
|
+
candidates = [item for item in candidates if item]
|
|
242
|
+
for candidate in candidates:
|
|
243
|
+
if skip_generic and _is_generic_component(candidate):
|
|
244
|
+
continue
|
|
245
|
+
if _is_uuid_component(candidate):
|
|
246
|
+
continue
|
|
247
|
+
return candidate
|
|
248
|
+
for candidate in candidates:
|
|
249
|
+
if skip_generic and (_is_generic_component(candidate) or _is_uuid_component(candidate)):
|
|
250
|
+
continue
|
|
251
|
+
return candidate
|
|
252
|
+
return _safe_name_component(fallback)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _dedupe_name_components(values: list[str]) -> list[str]:
|
|
256
|
+
components: list[str] = []
|
|
257
|
+
seen: set[str] = set()
|
|
258
|
+
for value in values:
|
|
259
|
+
component = _safe_name_component(value)
|
|
260
|
+
key = _component_key(component)
|
|
261
|
+
if not component or not key or key in seen:
|
|
262
|
+
continue
|
|
263
|
+
seen.add(key)
|
|
264
|
+
components.append(component)
|
|
265
|
+
return components
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _safe_name_component(value: str | None) -> str:
|
|
269
|
+
raw = str(value or "").strip()
|
|
270
|
+
if not raw:
|
|
271
|
+
return ""
|
|
272
|
+
ascii_raw = unicodedata.normalize("NFKD", raw).encode("ascii", "ignore").decode("ascii")
|
|
273
|
+
return safe_pipeline_name(ascii_raw or raw)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _component_key(value: str | None) -> str:
|
|
277
|
+
return _safe_name_component(value).strip("_").lower()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _is_generic_component(value: str | None) -> bool:
|
|
281
|
+
return _component_key(value) in _GENERIC_NAME_COMPONENTS
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _is_uuid_component(value: str | None) -> bool:
|
|
285
|
+
key = _component_key(value).replace("_", "-")
|
|
286
|
+
if len(key) != 36:
|
|
287
|
+
return False
|
|
288
|
+
for index, char in enumerate(key):
|
|
289
|
+
if index in {8, 13, 18, 23}:
|
|
290
|
+
if char != "-":
|
|
291
|
+
return False
|
|
292
|
+
continue
|
|
293
|
+
if char not in "0123456789abcdef":
|
|
294
|
+
return False
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
|
|
206
298
|
def _pick_choice(value: Any, *, allowed: set[str], default: str) -> str:
|
|
207
299
|
normalized = str(value or "").strip().lower()
|
|
208
300
|
if normalized in allowed:
|