Parse config.yaml into typed AppConfig dataclass

- ServerConfig, VideoConfig, ModelConfig, DeadBugConfig,
  AudioConfig, LoggingConfig as nested dataclasses
- Consumers use config.server.host, config.model.resolved_path etc.
- env var overrides preserved via _apply_env_overrides()
This commit is contained in:
2026-06-10 10:23:51 +08:00
parent c8fd057129
commit f9384f7bc1
7 changed files with 116 additions and 109 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
from __future__ import annotations
from app.diagnostics.crash_handler import enable_crash_handler
from configs.load import LOG_DIR
from configs.load import config
def startup() -> None:
enable_crash_handler(LOG_DIR)
enable_crash_handler(config.logging.dir_path)
from app.core.logging import setup_logging
setup_logging()
+6 -5
View File
@@ -4,16 +4,17 @@ from pathlib import Path
from loguru import logger
from configs.load import LOG_DIR, LOG_RETENTION, LOG_ROTATION
from configs.load import config
def setup_logging() -> None:
LOG_DIR.mkdir(parents=True, exist_ok=True)
log_dir = config.logging.dir_path
log_dir.mkdir(parents=True, exist_ok=True)
logger.add(
LOG_DIR / "posefit-server_{time:YYYY-MM-DD}.log",
rotation=LOG_ROTATION,
retention=LOG_RETENTION,
log_dir / "posefit-server_{time:YYYY-MM-DD}.log",
rotation=config.logging.rotation,
retention=config.logging.retention,
enqueue=True,
backtrace=True,
diagnose=True,
+8 -7
View File
@@ -1,10 +1,5 @@
from __future__ import annotations
import os
os.environ["MEDIAPIPE_DISABLE_LOGGING"] = "1"
os.environ["GLOG_minloglevel"] = "3"
import asyncio
from loguru import logger
@@ -16,8 +11,14 @@ from app.signaling.websocket_server import main as serve
def main():
startup()
logger.info("Starting server...")
asyncio.run(serve())
try:
asyncio.run(serve())
except (KeyboardInterrupt, SystemExit):
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Server error: {e}")
raise
if __name__ == "__main__":
main()
main()
+4 -3
View File
@@ -7,7 +7,7 @@ import websockets
from loguru import logger
from app.webrtc.peer_session import PeerSession
from configs.load import WS_HOST, WS_MAX_SIZE, WS_PORT
from configs.load import config
async def handle_client(websocket):
@@ -21,6 +21,7 @@ async def handle_client(websocket):
async def main():
logger.info(f"WebRTC signaling server: ws://{WS_HOST}:{WS_PORT}")
async with websockets.serve(handle_client, WS_HOST, WS_PORT, max_size=WS_MAX_SIZE):
cfg = config.server
logger.info(f"WebRTC signaling server: ws://{cfg.host}:{cfg.port}")
async with websockets.serve(handle_client, cfg.host, cfg.port, max_size=cfg.max_ws_size):
await asyncio.Future()
+11 -21
View File
@@ -10,17 +10,7 @@ from loguru import logger
from app.audio.rep_announcer import RepAnnouncer
from app.exercises.dead_bug.detector import DeadBugDetector
from app.rendering.window_display import close_window, is_esc_pressed, show_frame
from configs.load import (
EXTENSION_CONFIRM_FRAMES,
MODEL_PATH,
PREFER_GPU,
PROCESS_EVERY_N_FRAMES,
REP_ANNOUNCER_ENABLED,
REP_ANNOUNCER_RATE,
REP_ANNOUNCER_VOLUME,
RESET_CONFIRM_FRAMES,
VISIBILITY_THRESHOLD,
)
from configs.load import config
def _format_pose_debug(pose_result) -> str:
@@ -41,21 +31,21 @@ class VideoReceiver:
self._track = track
async def run(self) -> None:
logger.info("Start receiving video frames, process_every_n={}", PROCESS_EVERY_N_FRAMES)
logger.info("Start receiving video frames, process_every_n={}", config.video.process_every_n_frames)
frame_count = 0
processed_count = 0
detector = DeadBugDetector(
model_path=MODEL_PATH,
visibility_threshold=VISIBILITY_THRESHOLD,
extension_confirm_frames=EXTENSION_CONFIRM_FRAMES,
reset_confirm_frames=RESET_CONFIRM_FRAMES,
prefer_gpu=PREFER_GPU,
model_path=config.model.resolved_path,
visibility_threshold=config.dead_bug.visibility_threshold,
extension_confirm_frames=config.dead_bug.extension_confirm_frames,
reset_confirm_frames=config.dead_bug.reset_confirm_frames,
prefer_gpu=config.model.prefer_gpu,
)
announcer = RepAnnouncer(
enabled=REP_ANNOUNCER_ENABLED,
rate=REP_ANNOUNCER_RATE,
volume=REP_ANNOUNCER_VOLUME,
enabled=config.audio.rep_announcer_enabled,
rate=config.audio.rep_announcer_rate,
volume=config.audio.rep_announcer_volume,
)
last_announced_rep = 0
last_pose_result = None
@@ -68,7 +58,7 @@ class VideoReceiver:
raw_img = frame.to_ndarray(format="bgr24")
timestamp_ms = int(frame.time * 1000) if frame.time is not None else frame_count * 33
if frame_count % PROCESS_EVERY_N_FRAMES == 0 or last_pose_result is None:
if frame_count % config.video.process_every_n_frames == 0 or last_pose_result is None:
processed_count += 1
last_annotated, last_pose_result = detector.process_frame(raw_img, timestamp_ms)
if last_pose_result.rep_count > last_announced_rep: