Codebase review fixes: stability, performance, quality improvements
Stability: Add outer try/except/finally with _running=False cleanup to all 6
processing loop methods (live, color_strip, effect, audio, composite, mapped).
Add exponential backoff on consecutive capture errors in live_stream. Move
audio stream.stop() outside lock scope.
Performance: Replace per-pixel Python loop with np.array().tobytes() in
ddp_client. Vectorize pixelate filter with cv2.resize down+up. Vectorize
gradient rendering with np.searchsorted.
Frontend: Add lockBody/unlockBody re-entrancy counter. Add {once:true} to
fetchWithAuth abort listener. Null ws.onclose before ws.close() in LED preview.
Backend: Remove auth token prefix from log messages. Add atomic_write_json
helper (tempfile + os.replace) and update all 10 stores. Add name uniqueness
checks to all update methods. Fix DELETE status codes to 204 in audio_sources
and value_sources. Fix get_source() silent bug in color_strip_sources.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from wled_controller.storage.audio_source import (
|
||||
MonoAudioSource,
|
||||
MultichannelAudioSource,
|
||||
)
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -57,21 +57,14 @@ class AudioSourceStore:
|
||||
|
||||
def _save(self) -> None:
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
sources_dict = {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"audio_sources": sources_dict,
|
||||
"audio_sources": {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save audio sources to {self.file_path}: {e}")
|
||||
raise
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Dict, List, Optional
|
||||
|
||||
from wled_controller.core.audio.factory import AudioEngineRegistry
|
||||
from wled_controller.storage.audio_template import AudioCaptureTemplate
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -93,21 +93,14 @@ class AudioTemplateStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all templates to file."""
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
templates_dict = {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"templates": templates_dict,
|
||||
"templates": {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save audio templates to {self.file_path}: {e}")
|
||||
raise
|
||||
@@ -168,6 +161,9 @@ class AudioTemplateStore:
|
||||
template = self._templates[template_id]
|
||||
|
||||
if name is not None:
|
||||
for tid, t in self._templates.items():
|
||||
if tid != template_id and t.name == name:
|
||||
raise ValueError(f"Audio template with name '{name}' already exists")
|
||||
template.name = name
|
||||
if engine_type is not None:
|
||||
template.engine_type = engine_type
|
||||
|
||||
@@ -19,7 +19,7 @@ from wled_controller.storage.color_strip_source import (
|
||||
PictureColorStripSource,
|
||||
StaticColorStripSource,
|
||||
)
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -62,21 +62,14 @@ class ColorStripStore:
|
||||
|
||||
def _save(self) -> None:
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
sources_dict = {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"color_strip_sources": sources_dict,
|
||||
"color_strip_sources": {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save color strip sources to {self.file_path}: {e}")
|
||||
raise
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Dict, List, Optional
|
||||
|
||||
from wled_controller.storage.key_colors_picture_target import KeyColorRectangle
|
||||
from wled_controller.storage.pattern_template import PatternTemplate
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -88,21 +88,14 @@ class PatternTemplateStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all templates to file."""
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
templates_dict = {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"pattern_templates": templates_dict,
|
||||
"pattern_templates": {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save pattern templates to {self.file_path}: {e}")
|
||||
raise
|
||||
@@ -180,6 +173,9 @@ class PatternTemplateStore:
|
||||
template = self._templates[template_id]
|
||||
|
||||
if name is not None:
|
||||
for tid, t in self._templates.items():
|
||||
if tid != template_id and t.name == name:
|
||||
raise ValueError(f"Pattern template with name '{name}' already exists")
|
||||
template.name = name
|
||||
if rectangles is not None:
|
||||
template.rectangles = rectangles
|
||||
|
||||
@@ -12,7 +12,7 @@ from wled_controller.storage.picture_source import (
|
||||
ProcessedPictureSource,
|
||||
StaticImagePictureSource,
|
||||
)
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -68,21 +68,14 @@ class PictureSourceStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all streams to file."""
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
streams_dict = {
|
||||
stream_id: stream.to_dict()
|
||||
for stream_id, stream in self._streams.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"picture_sources": streams_dict,
|
||||
"picture_sources": {
|
||||
stream_id: stream.to_dict()
|
||||
for stream_id, stream in self._streams.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save picture sources to {self.file_path}: {e}")
|
||||
raise
|
||||
|
||||
@@ -12,7 +12,7 @@ from wled_controller.storage.key_colors_picture_target import (
|
||||
KeyColorsSettings,
|
||||
KeyColorsPictureTarget,
|
||||
)
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -63,21 +63,14 @@ class PictureTargetStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all targets to file."""
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
targets_dict = {
|
||||
target_id: target.to_dict()
|
||||
for target_id, target in self._targets.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"picture_targets": targets_dict,
|
||||
"picture_targets": {
|
||||
target_id: target.to_dict()
|
||||
for target_id, target in self._targets.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save picture targets to {self.file_path}: {e}")
|
||||
raise
|
||||
|
||||
@@ -10,7 +10,7 @@ from wled_controller.core.filters.filter_instance import FilterInstance
|
||||
from wled_controller.core.filters.registry import FilterRegistry
|
||||
from wled_controller.storage.picture_source import ProcessedPictureSource
|
||||
from wled_controller.storage.postprocessing_template import PostprocessingTemplate
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -92,21 +92,14 @@ class PostprocessingTemplateStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all templates to file."""
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
templates_dict = {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "2.0.0",
|
||||
"postprocessing_templates": templates_dict,
|
||||
"postprocessing_templates": {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save postprocessing templates to {self.file_path}: {e}")
|
||||
raise
|
||||
@@ -189,6 +182,9 @@ class PostprocessingTemplateStore:
|
||||
template = self._templates[template_id]
|
||||
|
||||
if name is not None:
|
||||
for tid, t in self._templates.items():
|
||||
if tid != template_id and t.name == name:
|
||||
raise ValueError(f"Postprocessing template with name '{name}' already exists")
|
||||
template.name = name
|
||||
if filters is not None:
|
||||
# Validate filter IDs
|
||||
|
||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from wled_controller.storage.profile import Condition, Profile
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -49,18 +49,13 @@ class ProfileStore:
|
||||
|
||||
def _save(self) -> None:
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"profiles": {
|
||||
pid: p.to_dict() for pid, p in self._profiles.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save profiles to {self.file_path}: {e}")
|
||||
raise
|
||||
@@ -81,6 +76,10 @@ class ProfileStore:
|
||||
conditions: Optional[List[Condition]] = None,
|
||||
target_ids: Optional[List[str]] = None,
|
||||
) -> Profile:
|
||||
for p in self._profiles.values():
|
||||
if p.name == name:
|
||||
raise ValueError(f"Profile with name '{name}' already exists")
|
||||
|
||||
profile_id = f"prof_{uuid.uuid4().hex[:8]}"
|
||||
now = datetime.utcnow()
|
||||
|
||||
@@ -116,6 +115,9 @@ class ProfileStore:
|
||||
profile = self._profiles[profile_id]
|
||||
|
||||
if name is not None:
|
||||
for pid, p in self._profiles.items():
|
||||
if pid != profile_id and p.name == name:
|
||||
raise ValueError(f"Profile with name '{name}' already exists")
|
||||
profile.name = name
|
||||
if enabled is not None:
|
||||
profile.enabled = enabled
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Dict, List, Optional
|
||||
|
||||
from wled_controller.core.capture_engines.factory import EngineRegistry
|
||||
from wled_controller.storage.template import CaptureTemplate
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -95,23 +95,14 @@ class TemplateStore:
|
||||
def _save(self) -> None:
|
||||
"""Save all templates to file."""
|
||||
try:
|
||||
# Ensure directory exists
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
templates_dict = {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"templates": templates_dict,
|
||||
"templates": {
|
||||
template_id: template.to_dict()
|
||||
for template_id, template in self._templates.items()
|
||||
},
|
||||
}
|
||||
|
||||
# Write to file
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save templates to {self.file_path}: {e}")
|
||||
raise
|
||||
@@ -218,6 +209,9 @@ class TemplateStore:
|
||||
|
||||
# Update fields
|
||||
if name is not None:
|
||||
for tid, t in self._templates.items():
|
||||
if tid != template_id and t.name == name:
|
||||
raise ValueError(f"Template with name '{name}' already exists")
|
||||
template.name = name
|
||||
if engine_type is not None:
|
||||
template.engine_type = engine_type
|
||||
|
||||
@@ -13,7 +13,7 @@ from wled_controller.storage.value_source import (
|
||||
StaticValueSource,
|
||||
ValueSource,
|
||||
)
|
||||
from wled_controller.utils import get_logger
|
||||
from wled_controller.utils import atomic_write_json, get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -59,21 +59,14 @@ class ValueSourceStore:
|
||||
|
||||
def _save(self) -> None:
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
sources_dict = {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
}
|
||||
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"value_sources": sources_dict,
|
||||
"value_sources": {
|
||||
sid: source.to_dict()
|
||||
for sid, source in self._sources.items()
|
||||
},
|
||||
}
|
||||
|
||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
atomic_write_json(self.file_path, data)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save value sources to {self.file_path}: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user