fix some bug in frontend
This commit is contained in:
+38
-5
@@ -57,6 +57,32 @@ def _clean_text(value: Optional[str]) -> Optional[str]:
|
|||||||
return cleaned or None
|
return cleaned or None
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_cfg_fields(cfg: AppConfig) -> bool:
|
||||||
|
changed = False
|
||||||
|
clean_api = _clean_text(cfg.mediamtx_api_url)
|
||||||
|
clean_webrtc = _clean_text(cfg.mediamtx_webrtc_url)
|
||||||
|
clean_user = _clean_text(cfg.mediamtx_api_user)
|
||||||
|
clean_pass = _clean_text(cfg.mediamtx_api_pass)
|
||||||
|
clean_recordings = _clean_text(cfg.recordings_dir)
|
||||||
|
|
||||||
|
if clean_api and clean_api != cfg.mediamtx_api_url:
|
||||||
|
cfg.mediamtx_api_url = clean_api
|
||||||
|
changed = True
|
||||||
|
if clean_webrtc and clean_webrtc != cfg.mediamtx_webrtc_url:
|
||||||
|
cfg.mediamtx_webrtc_url = clean_webrtc
|
||||||
|
changed = True
|
||||||
|
if clean_user != cfg.mediamtx_api_user:
|
||||||
|
cfg.mediamtx_api_user = clean_user
|
||||||
|
changed = True
|
||||||
|
if clean_pass != cfg.mediamtx_api_pass:
|
||||||
|
cfg.mediamtx_api_pass = clean_pass
|
||||||
|
changed = True
|
||||||
|
if clean_recordings and clean_recordings != cfg.recordings_dir:
|
||||||
|
cfg.recordings_dir = clean_recordings
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
def _load_mediamtx_yml() -> dict:
|
def _load_mediamtx_yml() -> dict:
|
||||||
if not MEDIAMTX_YML_PATH.exists():
|
if not MEDIAMTX_YML_PATH.exists():
|
||||||
raise HTTPException(status_code=500, detail="mediamtx_yml_not_found")
|
raise HTTPException(status_code=500, detail="mediamtx_yml_not_found")
|
||||||
@@ -96,6 +122,7 @@ def _build_mediamtx_view(data: dict, cfg: AppConfig) -> MediaMTXConfigView:
|
|||||||
|
|
||||||
async def _sync_app_config_from_mediamtx() -> AppConfig:
|
async def _sync_app_config_from_mediamtx() -> AppConfig:
|
||||||
cfg = await store.load()
|
cfg = await store.load()
|
||||||
|
_sanitize_cfg_fields(cfg)
|
||||||
data = _load_mediamtx_yml()
|
data = _load_mediamtx_yml()
|
||||||
cfg.cameras = [
|
cfg.cameras = [
|
||||||
Camera(name=c.name, rtsp_url=c.rtsp_url)
|
Camera(name=c.name, rtsp_url=c.rtsp_url)
|
||||||
@@ -133,6 +160,8 @@ def _restart_mediamtx() -> dict:
|
|||||||
|
|
||||||
async def _apply_recording(enabled: bool) -> None:
|
async def _apply_recording(enabled: bool) -> None:
|
||||||
cfg = await store.load()
|
cfg = await store.load()
|
||||||
|
if _sanitize_cfg_fields(cfg):
|
||||||
|
await store.save(cfg)
|
||||||
try:
|
try:
|
||||||
paths = await MediaMTXClient(
|
paths = await MediaMTXClient(
|
||||||
api_url=cfg.mediamtx_api_url,
|
api_url=cfg.mediamtx_api_url,
|
||||||
@@ -140,8 +169,9 @@ async def _apply_recording(enabled: bool) -> None:
|
|||||||
password=cfg.mediamtx_api_pass,
|
password=cfg.mediamtx_api_pass,
|
||||||
).list_paths_status()
|
).list_paths_status()
|
||||||
names = [it.get("name") for it in (paths.get("items") or []) if isinstance(it, dict) and it.get("name")]
|
names = [it.get("name") for it in (paths.get("items") or []) if isinstance(it, dict) and it.get("name")]
|
||||||
except Exception:
|
except httpx.HTTPError as e:
|
||||||
names = [c.name for c in cfg.cameras]
|
logger.warning("scheduler_skip_apply_recording: mediamtx_unreachable (%s)", type(e).__name__)
|
||||||
|
return
|
||||||
if not names:
|
if not names:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -150,7 +180,10 @@ async def _apply_recording(enabled: bool) -> None:
|
|||||||
username=cfg.mediamtx_api_user,
|
username=cfg.mediamtx_api_user,
|
||||||
password=cfg.mediamtx_api_pass,
|
password=cfg.mediamtx_api_pass,
|
||||||
)
|
)
|
||||||
await client.set_recording_bulk(names, enabled)
|
try:
|
||||||
|
await client.set_recording_bulk(names, enabled)
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.warning("scheduler_apply_recording_failed: %s", type(e).__name__)
|
||||||
|
|
||||||
|
|
||||||
scheduler = Scheduler(apply=_apply_recording)
|
scheduler = Scheduler(apply=_apply_recording)
|
||||||
@@ -169,9 +202,9 @@ async def _scheduler_loop() -> None:
|
|||||||
"Set mediamtx_api_user/mediamtx_api_pass in api/data/config.json"
|
"Set mediamtx_api_user/mediamtx_api_pass in api/data/config.json"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.exception("scheduler_tick_http_status_%s", code)
|
logger.warning("scheduler_tick_http_status_%s", code)
|
||||||
except httpx.HTTPError:
|
except httpx.HTTPError:
|
||||||
logger.exception("scheduler_tick_http_error")
|
logger.warning("scheduler_tick_http_error")
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("scheduler_tick_failed")
|
logger.exception("scheduler_tick_failed")
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>My Trae Project</title>
|
<title>IPCam OrangePi Dashboard</title>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
if (import.meta.hot?.on) {
|
if (import.meta.hot?.on) {
|
||||||
import.meta.hot.on('vite:error', (error) => {
|
import.meta.hot.on('vite:error', (error) => {
|
||||||
|
|||||||
+61
-1
@@ -11,4 +11,64 @@
|
|||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.light body {
|
||||||
|
background-color: rgb(244 244 245);
|
||||||
|
color: rgb(24 24 27);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme overrides for existing zinc-based utility classes in pages/cards/forms. */
|
||||||
|
.light .border-zinc-800 {
|
||||||
|
border-color: rgb(212 212 216);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .border-zinc-700 {
|
||||||
|
border-color: rgb(161 161 170);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .bg-zinc-900\/20,
|
||||||
|
.light .bg-zinc-900\/30 {
|
||||||
|
background-color: rgb(255 255 255 / 0.82);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .bg-zinc-950 {
|
||||||
|
background-color: rgb(255 255 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .bg-zinc-950\/10 {
|
||||||
|
background-color: rgb(244 244 245 / 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .bg-zinc-950\/30,
|
||||||
|
.light .bg-zinc-950\/40 {
|
||||||
|
background-color: rgb(228 228 231 / 0.78);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .hover\:bg-zinc-900:hover {
|
||||||
|
background-color: rgb(228 228 231);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .hover\:bg-zinc-950\/30:hover {
|
||||||
|
background-color: rgb(228 228 231 / 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .text-zinc-100,
|
||||||
|
.light .text-zinc-200 {
|
||||||
|
color: rgb(24 24 27);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .text-zinc-300,
|
||||||
|
.light .text-zinc-400 {
|
||||||
|
color: rgb(82 82 91);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .text-zinc-500 {
|
||||||
|
color: rgb(113 113 122);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light .from-zinc-950\/80 {
|
||||||
|
--tw-gradient-from: rgb(255 255 255 / 0.88) var(--tw-gradient-from-position);
|
||||||
|
--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user