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.
Files changed (59) hide show
  1. toposync_ext_streaming-0.1.2/README.md → toposync_ext_streaming-0.1.4/PKG-INFO +19 -0
  2. toposync_ext_streaming-0.1.2/PKG-INFO → toposync_ext_streaming-0.1.4/README.md +7 -12
  3. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/pyproject.toml +1 -1
  4. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/routes.py +4 -1
  5. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/extension.json +1 -1
  6. toposync_ext_streaming-0.1.4/src/toposync_ext_streaming/static/703.js +2 -0
  7. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/engine_manager.py +31 -1
  8. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_config.py +8 -0
  9. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/wizard/pipeline_builder.py +95 -3
  10. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/StreamingSettingsPanel.tsx +4 -4
  11. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/WizardCreatePipelineFromTransmission.tsx +8 -8
  12. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/translations.ts +14 -14
  13. toposync_ext_streaming-0.1.2/src/toposync_ext_streaming/static/703.js +0 -2
  14. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/.gitignore +0 -0
  15. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE +0 -0
  16. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE.ffmpeg +0 -0
  17. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/LICENSE.mediamtx +0 -0
  18. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/__init__.py +0 -0
  19. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/__init__.py +0 -0
  20. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/api/models.py +0 -0
  21. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/bin/ffmpeg/LICENSE +0 -0
  22. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/bin/mediamtx/LICENSE +0 -0
  23. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/pipelines/__init__.py +0 -0
  24. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/pipelines/operators.py +0 -0
  25. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/plugin.py +0 -0
  26. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/326.js +0 -0
  27. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/326.js.LICENSE.txt +0 -0
  28. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/4.js +0 -0
  29. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/4.js.LICENSE.txt +0 -0
  30. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/623.js +0 -0
  31. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/623.js.LICENSE.txt +0 -0
  32. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/703.js.LICENSE.txt +0 -0
  33. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/main.js +0 -0
  34. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/main.js.LICENSE.txt +0 -0
  35. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/static/remoteEntry.js +0 -0
  36. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/__init__.py +0 -0
  37. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/arbitration.py +0 -0
  38. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/camera_ingest.py +0 -0
  39. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/distributed_sync.py +0 -0
  40. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/ffmpeg_binary.py +0 -0
  41. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_api_client.py +0 -0
  42. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_binary.py +0 -0
  43. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/mediamtx_processes.py +0 -0
  44. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/placeholder.py +0 -0
  45. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/platform.py +0 -0
  46. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/publisher_manager.py +0 -0
  47. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/resize.py +0 -0
  48. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/runtime_state.py +0 -0
  49. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/streaming/writer_bridge.py +0 -0
  50. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/src/toposync_ext_streaming/wizard/__init__.py +0 -0
  51. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/package.json +0 -0
  52. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/activate.tsx +0 -0
  53. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/api/streamingApi.ts +0 -0
  54. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/constants.ts +0 -0
  55. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/entry.ts +0 -0
  56. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/settings/SubModal.tsx +0 -0
  57. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/src/types.ts +0 -0
  58. {toposync_ext_streaming-0.1.2 → toposync_ext_streaming-0.1.4}/ui/tsconfig.json +0 -0
  59. {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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "toposync-ext-streaming"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Toposync first-party extension: streaming settings, API surface, and pipeline sink bootstrap."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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)
@@ -2,7 +2,7 @@
2
2
  "schema_version": 1,
3
3
  "id": "com.toposync.streaming",
4
4
  "name": "Streaming",
5
- "version": "0.1.2",
5
+ "version": "0.1.4",
6
6
  "description": "Toposync extension for transmission and stream outputs.",
7
7
  "api_prefixes": ["/api/streams"],
8
8
  "frontend": {
@@ -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=":0",
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
- base = f"stream_{preset_id}__{camera_id}__{transmission_id}"
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": "rtmdet_det_small",
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": "rtmdet_det_small",
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: