Refactor core/ into logical sub-packages and split filter files
Reorganize the flat core/ directory (17 files) into three sub-packages: - core/devices/ — LED device communication (led_client, wled/adalight clients, providers, DDP) - core/processing/ — target processing pipeline (processor_manager, target processors, live streams, settings) - core/capture/ — screen capture & calibration (screen_capture, calibration, pixel_processor, overlay) Also split the monolithic filters/builtin.py (460 lines, 8 filters) into individual files: brightness, saturation, gamma, downscaler, pixelate, auto_crop, flip, color_correction. Includes the ProcessorManager refactor from target-centric architecture: ProcessorManager slimmed from ~1600 to ~490 lines with unified _processors dict replacing duplicate _targets/_kc_targets dicts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""Dependency injection for API routes."""
|
||||
|
||||
from wled_controller.core.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.template_store import TemplateStore
|
||||
from wled_controller.storage.postprocessing_template_store import PostprocessingTemplateStore
|
||||
|
||||
@@ -4,7 +4,7 @@ import httpx
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
|
||||
from wled_controller.api.auth import AuthRequired
|
||||
from wled_controller.core.led_client import (
|
||||
from wled_controller.core.devices.led_client import (
|
||||
get_all_providers,
|
||||
get_device_capabilities,
|
||||
get_provider,
|
||||
@@ -26,11 +26,11 @@ from wled_controller.api.schemas.devices import (
|
||||
DiscoveredDeviceResponse,
|
||||
DiscoverDevicesResponse,
|
||||
)
|
||||
from wled_controller.core.calibration import (
|
||||
from wled_controller.core.capture.calibration import (
|
||||
calibration_from_dict,
|
||||
calibration_to_dict,
|
||||
)
|
||||
from wled_controller.core.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.picture_target_store import PictureTargetStore
|
||||
from wled_controller.utils import get_logger
|
||||
@@ -50,6 +50,7 @@ def _device_to_response(device) -> DeviceResponse:
|
||||
led_count=device.led_count,
|
||||
enabled=device.enabled,
|
||||
baud_rate=device.baud_rate,
|
||||
auto_shutdown=device.auto_shutdown,
|
||||
capabilities=sorted(get_device_capabilities(device.device_type)),
|
||||
calibration=CalibrationSchema(**calibration_to_dict(device.calibration)),
|
||||
created_at=device.created_at,
|
||||
@@ -110,6 +111,11 @@ async def create_device(
|
||||
detail=f"Failed to connect to {device_type} device at {device_url}: {e}"
|
||||
)
|
||||
|
||||
# Resolve auto_shutdown default: True for adalight, False otherwise
|
||||
auto_shutdown = device_data.auto_shutdown
|
||||
if auto_shutdown is None:
|
||||
auto_shutdown = device_type == "adalight"
|
||||
|
||||
# Create device in storage
|
||||
device = store.create_device(
|
||||
name=device_data.name,
|
||||
@@ -117,6 +123,7 @@ async def create_device(
|
||||
led_count=led_count,
|
||||
device_type=device_type,
|
||||
baud_rate=device_data.baud_rate,
|
||||
auto_shutdown=auto_shutdown,
|
||||
)
|
||||
|
||||
# Register in processor manager for health monitoring
|
||||
@@ -127,6 +134,7 @@ async def create_device(
|
||||
calibration=device.calibration,
|
||||
device_type=device.device_type,
|
||||
baud_rate=device.baud_rate,
|
||||
auto_shutdown=device.auto_shutdown,
|
||||
)
|
||||
|
||||
return _device_to_response(device)
|
||||
@@ -233,6 +241,7 @@ async def update_device(
|
||||
enabled=update_data.enabled,
|
||||
led_count=update_data.led_count,
|
||||
baud_rate=update_data.baud_rate,
|
||||
auto_shutdown=update_data.auto_shutdown,
|
||||
)
|
||||
|
||||
# Sync connection info in processor manager
|
||||
@@ -246,6 +255,10 @@ async def update_device(
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Sync auto_shutdown in runtime state
|
||||
if update_data.auto_shutdown is not None and device_id in manager._devices:
|
||||
manager._devices[device_id].auto_shutdown = update_data.auto_shutdown
|
||||
|
||||
return _device_to_response(device)
|
||||
|
||||
except ValueError as e:
|
||||
@@ -409,7 +422,6 @@ async def set_device_power(
|
||||
body: dict,
|
||||
_auth: AuthRequired,
|
||||
store: DeviceStore = Depends(get_device_store),
|
||||
manager = Depends(get_processor_manager),
|
||||
):
|
||||
"""Turn device on or off."""
|
||||
device = store.get_device(device_id)
|
||||
@@ -423,13 +435,11 @@ async def set_device_power(
|
||||
raise HTTPException(status_code=400, detail="'on' must be a boolean")
|
||||
|
||||
try:
|
||||
if device.device_type == "adalight":
|
||||
if not on:
|
||||
await manager.send_black_frame(device_id)
|
||||
# "on" is a no-op for Adalight — next processing frame lights them up
|
||||
else:
|
||||
provider = get_provider(device.device_type)
|
||||
await provider.set_power(device.url, on)
|
||||
provider = get_provider(device.device_type)
|
||||
await provider.set_power(
|
||||
device.url, on,
|
||||
led_count=device.led_count, baud_rate=device.baud_rate,
|
||||
)
|
||||
return {"on": on}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set power for {device_id}: {e}")
|
||||
|
||||
@@ -35,7 +35,7 @@ from wled_controller.api.schemas.picture_sources import (
|
||||
)
|
||||
from wled_controller.core.capture_engines import EngineRegistry
|
||||
from wled_controller.core.filters import FilterRegistry, ImagePool
|
||||
from wled_controller.core.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.picture_target_store import PictureTargetStore
|
||||
from wled_controller.storage.template_store import TemplateStore
|
||||
|
||||
@@ -36,8 +36,9 @@ from wled_controller.api.schemas.picture_targets import (
|
||||
from wled_controller.config import get_config
|
||||
from wled_controller.core.capture_engines import EngineRegistry
|
||||
from wled_controller.core.filters import FilterRegistry, ImagePool
|
||||
from wled_controller.core.processor_manager import ProcessorManager, ProcessingSettings
|
||||
from wled_controller.core.screen_capture import (
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processing_settings import ProcessingSettings
|
||||
from wled_controller.core.capture.screen_capture import (
|
||||
calculate_average_color,
|
||||
calculate_dominant_color,
|
||||
calculate_median_color,
|
||||
@@ -276,25 +277,16 @@ async def update_target(
|
||||
description=data.description,
|
||||
)
|
||||
|
||||
# Sync processor manager
|
||||
if isinstance(target, WledPictureTarget):
|
||||
try:
|
||||
if data.settings is not None:
|
||||
manager.update_target_settings(target_id, target.settings)
|
||||
if data.picture_source_id is not None:
|
||||
manager.update_target_source(target_id, target.picture_source_id)
|
||||
if data.device_id is not None:
|
||||
manager.update_target_device(target_id, target.device_id)
|
||||
except ValueError:
|
||||
pass
|
||||
elif isinstance(target, KeyColorsPictureTarget):
|
||||
try:
|
||||
if data.key_colors_settings is not None:
|
||||
manager.update_kc_target_settings(target_id, target.settings)
|
||||
if data.picture_source_id is not None:
|
||||
manager.update_kc_target_source(target_id, target.picture_source_id)
|
||||
except ValueError:
|
||||
pass
|
||||
# Sync processor manager (unified API handles both target types)
|
||||
try:
|
||||
if data.settings is not None or data.key_colors_settings is not None:
|
||||
manager.update_target_settings(target_id, target.settings)
|
||||
if data.picture_source_id is not None:
|
||||
manager.update_target_source(target_id, target.picture_source_id)
|
||||
if data.device_id is not None and isinstance(target, WledPictureTarget):
|
||||
manager.update_target_device(target_id, target.device_id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return _target_to_response(target)
|
||||
|
||||
@@ -316,21 +308,15 @@ async def delete_target(
|
||||
):
|
||||
"""Delete a picture target. Stops processing first if active."""
|
||||
try:
|
||||
# Stop processing if running (WLED or KC)
|
||||
# Stop processing if running
|
||||
try:
|
||||
if manager.is_kc_target(target_id):
|
||||
await manager.stop_kc_processing(target_id)
|
||||
elif manager.is_target_processing(target_id):
|
||||
await manager.stop_processing(target_id)
|
||||
await manager.stop_processing(target_id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Remove from manager (WLED or KC)
|
||||
# Remove from manager
|
||||
try:
|
||||
if manager.is_kc_target(target_id):
|
||||
manager.remove_kc_target(target_id)
|
||||
else:
|
||||
manager.remove_target(target_id)
|
||||
manager.remove_target(target_id)
|
||||
except (ValueError, RuntimeError):
|
||||
pass
|
||||
|
||||
@@ -357,13 +343,10 @@ async def start_processing(
|
||||
):
|
||||
"""Start processing for a picture target."""
|
||||
try:
|
||||
# Verify target exists and dispatch by type
|
||||
target = target_store.get_target(target_id)
|
||||
# Verify target exists in store
|
||||
target_store.get_target(target_id)
|
||||
|
||||
if isinstance(target, KeyColorsPictureTarget):
|
||||
await manager.start_kc_processing(target_id)
|
||||
else:
|
||||
await manager.start_processing(target_id)
|
||||
await manager.start_processing(target_id)
|
||||
|
||||
logger.info(f"Started processing for target {target_id}")
|
||||
return {"status": "started", "target_id": target_id}
|
||||
@@ -385,10 +368,7 @@ async def stop_processing(
|
||||
):
|
||||
"""Stop processing for a picture target."""
|
||||
try:
|
||||
if manager.is_kc_target(target_id):
|
||||
await manager.stop_kc_processing(target_id)
|
||||
else:
|
||||
await manager.stop_processing(target_id)
|
||||
await manager.stop_processing(target_id)
|
||||
|
||||
logger.info(f"Stopped processing for target {target_id}")
|
||||
return {"status": "stopped", "target_id": target_id}
|
||||
@@ -410,10 +390,7 @@ async def get_target_state(
|
||||
):
|
||||
"""Get current processing state for a target."""
|
||||
try:
|
||||
if manager.is_kc_target(target_id):
|
||||
state = manager.get_kc_target_state(target_id)
|
||||
else:
|
||||
state = manager.get_target_state(target_id)
|
||||
state = manager.get_target_state(target_id)
|
||||
return TargetProcessingState(**state)
|
||||
|
||||
except ValueError as e:
|
||||
@@ -513,10 +490,7 @@ async def get_target_metrics(
|
||||
):
|
||||
"""Get processing metrics for a target."""
|
||||
try:
|
||||
if manager.is_kc_target(target_id):
|
||||
metrics = manager.get_kc_target_metrics(target_id)
|
||||
else:
|
||||
metrics = manager.get_target_metrics(target_id)
|
||||
metrics = manager.get_target_metrics(target_id)
|
||||
return TargetMetricsResponse(**metrics)
|
||||
|
||||
except ValueError as e:
|
||||
|
||||
@@ -32,7 +32,7 @@ from wled_controller.api.schemas.postprocessing import (
|
||||
)
|
||||
from wled_controller.core.capture_engines import EngineRegistry
|
||||
from wled_controller.core.filters import FilterRegistry, FilterInstance, ImagePool
|
||||
from wled_controller.core.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.template_store import TemplateStore
|
||||
from wled_controller.storage.postprocessing_template_store import PostprocessingTemplateStore
|
||||
|
||||
@@ -13,7 +13,7 @@ from wled_controller.api.schemas.system import (
|
||||
HealthResponse,
|
||||
VersionResponse,
|
||||
)
|
||||
from wled_controller.core.screen_capture import get_available_displays
|
||||
from wled_controller.core.capture.screen_capture import get_available_displays
|
||||
from wled_controller.utils import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -36,7 +36,7 @@ from wled_controller.api.schemas.filters import (
|
||||
)
|
||||
from wled_controller.core.capture_engines import EngineRegistry
|
||||
from wled_controller.core.filters import FilterRegistry
|
||||
from wled_controller.core.processor_manager import ProcessorManager
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.template_store import TemplateStore
|
||||
from wled_controller.storage.picture_source_store import PictureSourceStore
|
||||
|
||||
@@ -14,6 +14,7 @@ class DeviceCreate(BaseModel):
|
||||
device_type: str = Field(default="wled", description="LED device type (e.g., wled, adalight)")
|
||||
led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (required for adalight)")
|
||||
baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
|
||||
auto_shutdown: Optional[bool] = Field(default=None, description="Turn off device when server stops (defaults to true for adalight)")
|
||||
|
||||
|
||||
class DeviceUpdate(BaseModel):
|
||||
@@ -24,6 +25,7 @@ class DeviceUpdate(BaseModel):
|
||||
enabled: Optional[bool] = Field(None, description="Whether device is enabled")
|
||||
led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (for devices with manual_led_count capability)")
|
||||
baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
|
||||
auto_shutdown: Optional[bool] = Field(None, description="Turn off device when server stops")
|
||||
|
||||
|
||||
class Calibration(BaseModel):
|
||||
@@ -90,6 +92,7 @@ class DeviceResponse(BaseModel):
|
||||
led_count: int = Field(description="Total number of LEDs")
|
||||
enabled: bool = Field(description="Whether device is enabled")
|
||||
baud_rate: Optional[int] = Field(None, description="Serial baud rate")
|
||||
auto_shutdown: bool = Field(default=False, description="Turn off device when server stops")
|
||||
capabilities: List[str] = Field(default_factory=list, description="Device type capabilities")
|
||||
calibration: Optional[Calibration] = Field(None, description="Calibration configuration")
|
||||
created_at: datetime = Field(description="Creation timestamp")
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Dict, List, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from wled_controller.core.processor_manager import DEFAULT_STATE_CHECK_INTERVAL
|
||||
from wled_controller.core.processing.processing_settings import DEFAULT_STATE_CHECK_INTERVAL
|
||||
|
||||
|
||||
class ColorCorrection(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user