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>
5.8 KiB
5.8 KiB
Codebase Review — 2026-02-26
Findings from full codebase review. Items ordered by priority within each category.
Stability (Critical)
- Fatal loop exception leaks resources — Added outer
try/except/finallywithself._running = Falseto all 10 processing loop methods acrosslive_stream.py,color_strip_stream.py,effect_stream.py,audio_stream.py,composite_stream.py,mapped_stream.py. Also added per-iterationtry/exceptwhere missing. _is_runningflag cleanup — Fixed viafinally: self._running = Falsein all loop methods. (Race condition viathreading.Eventdeferred — current pattern sufficient with the finally block.)ColorStripStreamManagerthread safety — FALSE POSITIVE: All access is from the async event loop; methods are synchronous with noawaitpoints, so no preemption is possible.- Audio
stream.stop()called under lock — Movedstream.stop()outside lock scope in bothrelease()andrelease_all()inaudio_capture.py. - WS accept-before-validate — FALSE POSITIVE: All WS endpoints validate auth and resolve configs BEFORE calling
websocket.accept(). - Capture error no-backoff — Added consecutive error counter with exponential backoff (
min(1.0, 0.1 * (errors - 5))) inScreenCaptureLiveStream._capture_loop(). - WGC session close not detected — Deferred (Windows-specific edge case, low priority).
LiveStreamManager.acquire()not thread-safe — FALSE POSITIVE: Same as ColorStripStreamManager — all access from async event loop, no await in methods.
Performance (High Impact)
- Per-pixel Python loop in
send_pixels()— Replaced per-pixel Python loop withnp.array().tobytes()inddp_client.py. Hot path already usessend_pixels_numpy(). - WGC 6MB frame allocation per callback — Deferred (Windows-specific, requires WGC API changes).
- Gradient rendering O(LEDs×Stops) Python loop — Vectorized with NumPy:
np.searchsortedfor stop lookup + vectorized interpolation in_compute_gradient_colors(). PixelateFilternested Python loop — Replaced withcv2.resizedown (INTER_AREA) + up (INTER_NEAREST) — pure C++ backend.DownscalerFilterdouble allocation — FALSE POSITIVE: Already uses singlecv2.resize()call (vectorized C++).SaturationFilter~25MB temp arrays — FALSE POSITIVE: Already uses pre-allocated scratch buffer and vectorized in-place numpy.FrameInterpolationFiltercopies full image — FALSE POSITIVE: Already uses vectorized numpy integer blending with image pool.datetime.utcnow()per frame — LOW IMPACT: ~1-2μs per call, negligible at 60fps. Deprecation tracked under Backend Quality.- Unbounded diagnostic lists — FALSE POSITIVE: Lists are cleared every 5 seconds (~300 entries max at 60fps). Trivial memory.
Frontend Quality
lockBody()/unlockBody()not re-entrant — Added_lockCountreference counter and_savedScrollYinui.js. First lock saves scroll, last unlock restores.- XSS via unescaped engine config keys — FALSE POSITIVE: Both capture template and audio template card renderers already use
escapeHtml()on keys and values. - LED preview WS
onclosenot nulled — Addedws.onclose = nullbeforews.close()indisconnectLedPreviewWS()intargets.js. fetchWithAuthretry adds duplicate listeners — Added{ once: true }to abort signal listener inapi.js.- Audio
requestAnimationFrameloop continues after WS close — FALSE POSITIVE: Loop already checkstestAudioModal.isOpenbefore scheduling next frame, and_cleanupTest()cancels the animation frame.
Backend Quality
- No thread-safety in
JsonStore— Deferred (low risk — all stores are accessed from async event loop). - Auth token prefix logged — Removed token prefix from log message in
auth.py. Now logs only "Invalid API key attempt". - Duplicate capture/test code — Deferred (code duplication, not a bug — refactoring would reduce LOC but doesn't fix a defect).
- Update methods allow duplicate names — Added name uniqueness checks to
update_templateintemplate_store.py,postprocessing_template_store.py,audio_template_store.py,pattern_template_store.py, andupdate_profileinprofile_store.py. Also added missing check tocreate_profile. - Routes access
manager._privateattrs — Deferred (stylistic, not a bug — would require adding public accessor methods). - Non-atomic file writes — Created
utils/file_ops.pywithatomic_write_json()helper (tempfile +os.replace). Updated all 10 store files. - 444 f-string logger calls — Deferred (performance impact negligible — Python evaluates f-strings very fast; lazy
%sformatting only matters at very high call rates). get_source()silent bug — Fixed:color_strip_sources.py:_resolve_display_index()calledpicture_source_store.get_source()which doesn't exist (should beget_stream()). Was silently returning0for display index.get_config()race — Deferred (low risk — config changes are infrequent user-initiated operations).datetime.utcnow()deprecated — Deferred (functional, deprecation warning only appears in Python 3.12+).- Inconsistent DELETE status codes — Changed
audio_sources.pyandvalue_sources.pyDELETE endpoints from 200 to 204 (matching all other DELETE endpoints).
Architecture (Observations, no action needed)
Strengths: Clean layered design, plugin registries, reference-counted stream sharing, consistent API patterns.
Weaknesses: No backpressure (slow consumers buffer frames), thread count grows linearly, config global singleton, reference counting races.