Refactor capture engine architecture, rename PictureStream to PictureSource, and split API modules

- Separate CaptureEngine into stateless factory + stateful CaptureStream session
- Add LiveStream/LiveStreamManager for shared capture with reference counting
- Rename PictureStream to PictureSource across storage, API, and UI
- Remove legacy migration logic and unused compatibility code
- Split monolithic routes.py (1935 lines) into 5 focused route modules
- Split schemas.py (480 lines) into 7 schema modules with re-exports
- Extract dependency injection into dedicated dependencies.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 14:27:00 +03:00
parent b8389f080a
commit c3828e10fa
42 changed files with 4047 additions and 3797 deletions

View File

@@ -30,8 +30,7 @@ class Device:
enabled: bool = True,
settings: Optional[ProcessingSettings] = None,
calibration: Optional[CalibrationConfig] = None,
capture_template_id: str = "",
picture_stream_id: str = "",
picture_source_id: str = "",
created_at: Optional[datetime] = None,
updated_at: Optional[datetime] = None,
):
@@ -45,8 +44,7 @@ class Device:
enabled: Whether device is enabled
settings: Processing settings
calibration: Calibration configuration
capture_template_id: ID of assigned capture template (legacy, use picture_stream_id)
picture_stream_id: ID of assigned picture stream
picture_source_id: ID of assigned picture source
created_at: Creation timestamp
updated_at: Last update timestamp
"""
@@ -57,8 +55,7 @@ class Device:
self.enabled = enabled
self.settings = settings or ProcessingSettings()
self.calibration = calibration or create_default_calibration(led_count)
self.capture_template_id = capture_template_id
self.picture_stream_id = picture_stream_id
self.picture_source_id = picture_source_id
self.created_at = created_at or datetime.utcnow()
self.updated_at = updated_at or datetime.utcnow()
@@ -86,8 +83,7 @@ class Device:
"state_check_interval": self.settings.state_check_interval,
},
"calibration": calibration_to_dict(self.calibration),
"capture_template_id": self.capture_template_id,
"picture_stream_id": self.picture_stream_id,
"picture_source_id": self.picture_source_id,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
@@ -112,10 +108,7 @@ class Device:
saturation=settings_data.get("saturation", 1.0),
smoothing=settings_data.get("smoothing", 0.3),
interpolation_mode=settings_data.get("interpolation_mode", "average"),
state_check_interval=settings_data.get(
"state_check_interval",
settings_data.get("health_check_interval", DEFAULT_STATE_CHECK_INTERVAL),
),
state_check_interval=settings_data.get("state_check_interval", DEFAULT_STATE_CHECK_INTERVAL),
)
calibration_data = data.get("calibration")
@@ -125,8 +118,7 @@ class Device:
else create_default_calibration(data["led_count"])
)
capture_template_id = data.get("capture_template_id", "")
picture_stream_id = data.get("picture_stream_id", "")
picture_source_id = data.get("picture_source_id", "")
return cls(
device_id=data["id"],
@@ -136,8 +128,7 @@ class Device:
enabled=data.get("enabled", True),
settings=settings,
calibration=calibration,
capture_template_id=capture_template_id,
picture_stream_id=picture_stream_id,
picture_source_id=picture_source_id,
created_at=datetime.fromisoformat(data.get("created_at", datetime.utcnow().isoformat())),
updated_at=datetime.fromisoformat(data.get("updated_at", datetime.utcnow().isoformat())),
)
@@ -219,8 +210,7 @@ class DeviceStore:
led_count: int,
settings: Optional[ProcessingSettings] = None,
calibration: Optional[CalibrationConfig] = None,
capture_template_id: str = "",
picture_stream_id: str = "",
picture_source_id: str = "",
) -> Device:
"""Create a new device.
@@ -230,7 +220,7 @@ class DeviceStore:
led_count: Number of LEDs
settings: Processing settings
calibration: Calibration configuration
capture_template_id: ID of assigned capture template
picture_source_id: ID of assigned picture source
Returns:
Created device
@@ -249,8 +239,7 @@ class DeviceStore:
led_count=led_count,
settings=settings,
calibration=calibration,
capture_template_id=capture_template_id,
picture_stream_id=picture_stream_id,
picture_source_id=picture_source_id,
)
# Store
@@ -288,8 +277,7 @@ class DeviceStore:
enabled: Optional[bool] = None,
settings: Optional[ProcessingSettings] = None,
calibration: Optional[CalibrationConfig] = None,
capture_template_id: Optional[str] = None,
picture_stream_id: Optional[str] = None,
picture_source_id: Optional[str] = None,
) -> Device:
"""Update device.
@@ -301,7 +289,7 @@ class DeviceStore:
enabled: New enabled state (optional)
settings: New settings (optional)
calibration: New calibration (optional)
capture_template_id: New capture template ID (optional)
picture_source_id: New picture source ID (optional)
Returns:
Updated device
@@ -334,10 +322,8 @@ class DeviceStore:
f"does not match device LED count ({device.led_count})"
)
device.calibration = calibration
if capture_template_id is not None:
device.capture_template_id = capture_template_id
if picture_stream_id is not None:
device.picture_stream_id = picture_stream_id
if picture_source_id is not None:
device.picture_source_id = picture_source_id
device.updated_at = datetime.utcnow()