fix one machine only
This commit is contained in:
+37
-70
@@ -5,7 +5,6 @@ import logging
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
import yaml
|
||||
@@ -17,6 +16,7 @@ from .config_store import default_store
|
||||
from .mediamtx_client import MediaMTXClient
|
||||
from .models import (
|
||||
AppConfig,
|
||||
AppConfigUpdate,
|
||||
Camera,
|
||||
MediaMTXAddCameraRequest,
|
||||
MediaMTXCamera,
|
||||
@@ -50,51 +50,11 @@ app.add_middleware(
|
||||
store = default_store()
|
||||
|
||||
|
||||
def _extract_port(address: str, fallback: int) -> int:
|
||||
if not address:
|
||||
return fallback
|
||||
text = str(address).strip()
|
||||
if text.startswith(":"):
|
||||
text = text[1:]
|
||||
if ":" in text:
|
||||
text = text.rsplit(":", 1)[-1]
|
||||
try:
|
||||
value = int(text)
|
||||
return value if 1 <= value <= 65535 else fallback
|
||||
except ValueError:
|
||||
return fallback
|
||||
|
||||
|
||||
def _host_from_url(url: str, fallback: str) -> str:
|
||||
try:
|
||||
parsed = urlparse(url)
|
||||
host = (parsed.hostname or "").strip()
|
||||
if host and host not in {"0.0.0.0", "::"}:
|
||||
return host
|
||||
except ValueError:
|
||||
pass
|
||||
return fallback
|
||||
|
||||
|
||||
def _extract_host(address: str, fallback: str) -> str:
|
||||
text = str(address or "").strip()
|
||||
if not text:
|
||||
return fallback
|
||||
if text.startswith(":"):
|
||||
return fallback
|
||||
if "://" in text:
|
||||
return _host_from_url(text, fallback)
|
||||
if text.startswith("[") and "]" in text:
|
||||
host = text[1 : text.index("]")]
|
||||
return host or fallback
|
||||
if ":" in text:
|
||||
host = text.rsplit(":", 1)[0].strip()
|
||||
if host and host not in {"0.0.0.0", "::"}:
|
||||
return host
|
||||
return fallback
|
||||
if text in {"0.0.0.0", "::"}:
|
||||
return fallback
|
||||
return text
|
||||
def _clean_text(value: Optional[str]) -> Optional[str]:
|
||||
if value is None:
|
||||
return None
|
||||
cleaned = str(value).strip().strip("`'\"")
|
||||
return cleaned or None
|
||||
|
||||
|
||||
def _load_mediamtx_yml() -> dict:
|
||||
@@ -111,33 +71,24 @@ def _save_mediamtx_yml(data: dict) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _build_mediamtx_view(data: dict, current_cfg: Optional[AppConfig] = None) -> MediaMTXConfigView:
|
||||
api_port = _extract_port(str(data.get("apiAddress", ":9997")), 9997)
|
||||
webrtc_port = _extract_port(str(data.get("webrtcAddress", ":8889")), 8889)
|
||||
current_api_host = _host_from_url((current_cfg.mediamtx_api_url if current_cfg else ""), "127.0.0.1")
|
||||
current_webrtc_host = _host_from_url((current_cfg.mediamtx_webrtc_url if current_cfg else ""), "127.0.0.1")
|
||||
api_host = _extract_host(str(data.get("apiAddress", ":9997")), current_api_host)
|
||||
|
||||
hosts = data.get("webrtcAdditionalHosts") or []
|
||||
host = hosts[0] if isinstance(hosts, list) and hosts else _extract_host(str(data.get("webrtcAddress", ":8889")), current_webrtc_host)
|
||||
|
||||
def _build_mediamtx_view(data: dict, cfg: AppConfig) -> MediaMTXConfigView:
|
||||
path_defaults = data.get("pathDefaults") or {}
|
||||
record_enabled = bool(path_defaults.get("record", False))
|
||||
|
||||
cameras: list[MediaMTXCamera] = []
|
||||
paths = data.get("paths") or {}
|
||||
if isinstance(paths, dict):
|
||||
for name, cfg in paths.items():
|
||||
if not isinstance(cfg, dict):
|
||||
for name, path_cfg in paths.items():
|
||||
if not isinstance(path_cfg, dict):
|
||||
continue
|
||||
source = cfg.get("source")
|
||||
source = path_cfg.get("source")
|
||||
if isinstance(source, str) and source.strip():
|
||||
cameras.append(MediaMTXCamera(name=str(name), rtsp_url=source))
|
||||
cameras.sort(key=lambda x: x.name)
|
||||
|
||||
return MediaMTXConfigView(
|
||||
api_url=f"http://{api_host}:{api_port}",
|
||||
webrtc_url=f"http://{host}:{webrtc_port}",
|
||||
api_url=cfg.mediamtx_api_url,
|
||||
webrtc_url=cfg.mediamtx_webrtc_url,
|
||||
record_enabled=record_enabled,
|
||||
cameras=cameras,
|
||||
)
|
||||
@@ -146,11 +97,10 @@ def _build_mediamtx_view(data: dict, current_cfg: Optional[AppConfig] = None) ->
|
||||
async def _sync_app_config_from_mediamtx() -> AppConfig:
|
||||
cfg = await store.load()
|
||||
data = _load_mediamtx_yml()
|
||||
view = _build_mediamtx_view(data, cfg)
|
||||
|
||||
cfg.mediamtx_api_url = view.api_url
|
||||
cfg.mediamtx_webrtc_url = view.webrtc_url
|
||||
cfg.cameras = [Camera(name=c.name, rtsp_url=c.rtsp_url) for c in view.cameras]
|
||||
cfg.cameras = [
|
||||
Camera(name=c.name, rtsp_url=c.rtsp_url)
|
||||
for c in _build_mediamtx_view(data, cfg).cameras
|
||||
]
|
||||
await store.save(cfg)
|
||||
return cfg
|
||||
|
||||
@@ -274,14 +224,29 @@ async def get_config() -> AppConfig:
|
||||
|
||||
@app.get("/api/mediamtx/config")
|
||||
async def get_mediamtx_config() -> MediaMTXConfigView:
|
||||
cfg = await store.load()
|
||||
data = _load_mediamtx_yml()
|
||||
view = _build_mediamtx_view(data, await store.load())
|
||||
view = _build_mediamtx_view(data, cfg)
|
||||
await _sync_app_config_from_mediamtx()
|
||||
return view
|
||||
|
||||
|
||||
@app.post("/api/config/basic")
|
||||
async def update_basic_config(payload: AppConfigUpdate) -> AppConfig:
|
||||
cfg = await store.load()
|
||||
cfg.mediamtx_api_url = _clean_text(payload.mediamtx_api_url) or cfg.mediamtx_api_url
|
||||
cfg.mediamtx_webrtc_url = _clean_text(payload.mediamtx_webrtc_url) or cfg.mediamtx_webrtc_url
|
||||
cfg.mediamtx_api_user = _clean_text(payload.mediamtx_api_user)
|
||||
cfg.mediamtx_api_pass = _clean_text(payload.mediamtx_api_pass)
|
||||
cfg.recordings_dir = _clean_text(payload.recordings_dir) or cfg.recordings_dir
|
||||
cfg.api_port = payload.api_port
|
||||
await store.save(cfg)
|
||||
return await _sync_app_config_from_mediamtx()
|
||||
|
||||
|
||||
@app.post("/api/mediamtx/cameras")
|
||||
async def add_mediamtx_camera(payload: MediaMTXAddCameraRequest) -> MediaMTXConfigView:
|
||||
cfg = await store.load()
|
||||
data = _load_mediamtx_yml()
|
||||
paths = data.setdefault("paths", {})
|
||||
if not isinstance(paths, dict):
|
||||
@@ -295,11 +260,12 @@ async def add_mediamtx_camera(payload: MediaMTXAddCameraRequest) -> MediaMTXConf
|
||||
paths[name] = {"source": payload.rtsp_url.strip()}
|
||||
_save_mediamtx_yml(data)
|
||||
await _sync_app_config_from_mediamtx()
|
||||
return _build_mediamtx_view(data, await store.load())
|
||||
return _build_mediamtx_view(data, cfg)
|
||||
|
||||
|
||||
@app.delete("/api/mediamtx/cameras/{name}")
|
||||
async def delete_mediamtx_camera(name: str) -> MediaMTXConfigView:
|
||||
cfg = await store.load()
|
||||
data = _load_mediamtx_yml()
|
||||
paths = data.get("paths") or {}
|
||||
if not isinstance(paths, dict) or name not in paths:
|
||||
@@ -308,11 +274,12 @@ async def delete_mediamtx_camera(name: str) -> MediaMTXConfigView:
|
||||
data["paths"] = paths
|
||||
_save_mediamtx_yml(data)
|
||||
await _sync_app_config_from_mediamtx()
|
||||
return _build_mediamtx_view(data, await store.load())
|
||||
return _build_mediamtx_view(data, cfg)
|
||||
|
||||
|
||||
@app.post("/api/mediamtx/recording")
|
||||
async def set_mediamtx_recording(data: RecordingToggle) -> MediaMTXConfigView:
|
||||
cfg = await store.load()
|
||||
payload = _load_mediamtx_yml()
|
||||
path_defaults = payload.setdefault("pathDefaults", {})
|
||||
if not isinstance(path_defaults, dict):
|
||||
@@ -320,7 +287,7 @@ async def set_mediamtx_recording(data: RecordingToggle) -> MediaMTXConfigView:
|
||||
path_defaults["record"] = bool(data.enabled)
|
||||
payload["pathDefaults"] = path_defaults
|
||||
_save_mediamtx_yml(payload)
|
||||
return _build_mediamtx_view(payload, await store.load())
|
||||
return _build_mediamtx_view(payload, cfg)
|
||||
|
||||
|
||||
@app.post("/api/mediamtx/restart")
|
||||
|
||||
Reference in New Issue
Block a user