mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 23:05:10 +00:00
fix(plugins): web mode lookups use the modes the display registered (#668)
A plugin may compute its display modes from its config: soccer-scoreboard registers soccer_<league>_live/recent/upcoming for every custom_leagues entry, which no manifest can list ahead of time. The display always rotated them (_register_loaded_plugin prefers plugin.modes), but the web process reads plugins as files, so /display/modes, the on-demand dialog and on-demand/start with a mode and no plugin_id saw only manifests -- a custom league's mode was missing from every list and 404'd on lookup. - PluginStateManager.record_modes(): the controller records what it registered, on the loaded record (an unload or reload forgets it) - the runtime snapshot carries it per plugin as "modes" (bounded), and PluginRuntimeView.display_modes() reports it only while live - PluginCatalog takes a runtime_source; get_plugin_display_modes and find_plugin_for_mode prefer the live modes, falling back to the manifest when the display is stopped or has not loaded the plugin. The view is read at most once a second, so a listing is one read, not one per plugin. No manifest or plugin change needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ snapshot ``plugin_runtime.PluginRuntimePublisher`` publishes from it.
|
||||
import threading
|
||||
import time
|
||||
from enum import Enum
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
@@ -231,6 +231,26 @@ class PluginStateManager:
|
||||
}
|
||||
self._note_change()
|
||||
|
||||
def record_modes(self, plugin_id: str, modes: List[str]) -> None:
|
||||
"""Record the display modes the display registered for ``plugin_id``.
|
||||
|
||||
Called by the DisplayController each time it registers the plugin.
|
||||
These are the modes it actually rotates and accepts on-demand --
|
||||
``plugin.modes`` when the plugin computes them (a soccer league the
|
||||
user added under ``custom_leagues``), else the manifest's list -- and
|
||||
the web interface has no other way to learn them (#668). Kept on the
|
||||
loaded record, so an unload or a reload's fresh record_loaded()
|
||||
forgets them until the plugin is registered again.
|
||||
"""
|
||||
with self._lock:
|
||||
loaded = self._loaded.get(plugin_id)
|
||||
if loaded is None:
|
||||
return
|
||||
modes = [str(m) for m in modes]
|
||||
if loaded.get('modes') != modes:
|
||||
loaded['modes'] = modes
|
||||
self._note_change()
|
||||
|
||||
def record_unloaded(self, plugin_id: str) -> None:
|
||||
"""Forget the loaded record alone, keeping state and error info: for
|
||||
an unload that failed after the instance was already dropped."""
|
||||
@@ -243,7 +263,8 @@ class PluginStateManager:
|
||||
section so a concurrent load or unload is seen whole or not at all.
|
||||
|
||||
Per plugin: ``state`` (published_state()'s value), ``loaded``,
|
||||
``version`` and ``loaded_at`` (None unless loaded) and ``error_info``
|
||||
``version``, ``loaded_at`` and ``modes`` (None unless loaded; ``modes``
|
||||
also None until the display registers it) and ``error_info``
|
||||
(a copy, or None).
|
||||
"""
|
||||
with self._lock:
|
||||
@@ -257,6 +278,7 @@ class PluginStateManager:
|
||||
'loaded': loaded is not None,
|
||||
'version': loaded['version'] if loaded else None,
|
||||
'loaded_at': loaded['loaded_at'] if loaded else None,
|
||||
'modes': list(loaded['modes']) if loaded and 'modes' in loaded else None,
|
||||
'error_info': dict(info) if info is not None else None,
|
||||
}
|
||||
return records
|
||||
|
||||
Reference in New Issue
Block a user