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:
2026-02-18 12:03:29 +03:00
parent 77dd342c4c
commit fc779eef39
50 changed files with 2740 additions and 2267 deletions

View File

@@ -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: