Nightly: deterministic plugin loading and docs refresh
This commit is contained in:
@@ -1230,11 +1230,16 @@ def _discover_plugins() -> dict[str, BasisPlugin]:
|
||||
except Exception as exc:
|
||||
xbmc.log(f"Plugin-Datei {file_path.name} konnte nicht geladen werden: {exc}", xbmc.LOGWARNING)
|
||||
continue
|
||||
plugin_classes = [
|
||||
obj
|
||||
for obj in module.__dict__.values()
|
||||
if inspect.isclass(obj) and issubclass(obj, BasisPlugin) and obj is not BasisPlugin
|
||||
]
|
||||
preferred = getattr(module, "Plugin", None)
|
||||
if inspect.isclass(preferred) and issubclass(preferred, BasisPlugin) and preferred is not BasisPlugin:
|
||||
plugin_classes = [preferred]
|
||||
else:
|
||||
plugin_classes = [
|
||||
obj
|
||||
for obj in module.__dict__.values()
|
||||
if inspect.isclass(obj) and issubclass(obj, BasisPlugin) and obj is not BasisPlugin
|
||||
]
|
||||
plugin_classes.sort(key=lambda cls: cls.__name__.casefold())
|
||||
for cls in plugin_classes:
|
||||
try:
|
||||
instance = cls()
|
||||
@@ -1245,7 +1250,21 @@ def _discover_plugins() -> dict[str, BasisPlugin]:
|
||||
reason = getattr(instance, "unavailable_reason", "Nicht verfuegbar.")
|
||||
xbmc.log(f"Plugin {cls.__name__} deaktiviert: {reason}", xbmc.LOGWARNING)
|
||||
continue
|
||||
plugins[instance.name] = instance
|
||||
plugin_name = str(getattr(instance, "name", "") or "").strip()
|
||||
if not plugin_name:
|
||||
xbmc.log(
|
||||
f"Plugin {cls.__name__} wurde ohne Name registriert und wird uebersprungen.",
|
||||
xbmc.LOGWARNING,
|
||||
)
|
||||
continue
|
||||
if plugin_name in plugins:
|
||||
xbmc.log(
|
||||
f"Plugin-Name doppelt ({plugin_name}), {cls.__name__} wird uebersprungen.",
|
||||
xbmc.LOGWARNING,
|
||||
)
|
||||
continue
|
||||
plugins[plugin_name] = instance
|
||||
plugins = dict(sorted(plugins.items(), key=lambda item: item[0].casefold()))
|
||||
_PLUGIN_CACHE = plugins
|
||||
return plugins
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Set
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
|
||||
class BasisPlugin(ABC):
|
||||
@@ -12,6 +12,7 @@ class BasisPlugin(ABC):
|
||||
|
||||
name: str
|
||||
version: str = "0.0.0"
|
||||
prefer_source_metadata: bool = False
|
||||
|
||||
@abstractmethod
|
||||
async def search_titles(self, query: str) -> List[str]:
|
||||
@@ -29,6 +30,10 @@ class BasisPlugin(ABC):
|
||||
"""Optional: Liefert den Stream-Link fuer eine konkrete Folge."""
|
||||
return None
|
||||
|
||||
def metadata_for(self, title: str) -> Tuple[Dict[str, str], Dict[str, str], Optional[List[Any]]]:
|
||||
"""Optional: Liefert Info-Labels, Art und Cast fuer einen Titel."""
|
||||
return {}, {}, None
|
||||
|
||||
def resolve_stream_link(self, link: str) -> Optional[str]:
|
||||
"""Optional: Folgt einem Stream-Link und liefert die finale URL."""
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user