Core & Architektur: - Neues Verzeichnis addon/core/ mit router.py, trakt.py, metadata.py, gui.py, playstate.py, plugin_manager.py, updater.py - Tests-Verzeichnis hinzugefügt (24 Tests, pytest + Coverage) Trakt-Integration: - OAuth Device Flow, Scrobbling, Watchlist, History, Calendar - Upcoming Episodes, Weiterschauen (Continue Watching) - Watched-Status in Episodenlisten - _trakt_find_in_plugins() mit 5-Min-Cache Serienstream-Suche: - API-Ergebnisse werden immer mit Katalog-Cache ergänzt (serverseitiges 10-Treffer-Limit) - Katalog-Cache wird beim Addon-Start im Daemon-Thread vorgewärmt - Notification nach Cache-Load via xbmc.executebuiltin() (thread-sicher) Bugfixes (Code-Review): - Race Condition auf _TRAKT_WATCHED_CACHE: _TRAKT_WATCHED_CACHE_LOCK hinzugefügt - GUI-Dialog aus Daemon-Thread: xbmcgui -> xbmc.executebuiltin() - ValueError in Trakt-Watchlist-Routen abgesichert - Token expires_at==0 Check korrigiert - get_setting_bool() Kontrollfluss in gui.py bereinigt - topstreamfilm_plugin: try-finally um xbmcvfs.File.close() Cleanup: - default.py.bak und refactor_router.py entfernt - .gitignore: /tests/ Eintrag entfernt - Type-Hints vereinheitlicht (Dict/List/Tuple -> dict/list/tuple)
154 lines
5.1 KiB
Python
154 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
||
"""Gemeinsame Schnittstelle fuer Kodi-Plugins."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from abc import ABC, abstractmethod
|
||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
||
|
||
|
||
class BasisPlugin(ABC):
|
||
"""Abstrakte Basisklasse fuer alle Integrationen."""
|
||
|
||
name: str
|
||
version: str = "0.0.0"
|
||
prefer_source_metadata: bool = False
|
||
|
||
@abstractmethod
|
||
async def search_titles(
|
||
self,
|
||
query: str,
|
||
progress_callback: Optional[Callable[[str, Optional[int]], Any]] = None,
|
||
) -> List[str]:
|
||
"""Liefert eine Liste aller Treffer fuer die Suche."""
|
||
|
||
@abstractmethod
|
||
def seasons_for(self, title: str) -> List[str]:
|
||
"""Liefert alle Staffeln zu einem Titel."""
|
||
|
||
@abstractmethod
|
||
def episodes_for(self, title: str, season: str) -> List[str]:
|
||
"""Liefert alle Folgen zu einer Staffel."""
|
||
|
||
def stream_link_for(self, title: str, season: str, episode: str) -> Optional[str]:
|
||
"""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
|
||
|
||
def genres(self) -> List[str]:
|
||
"""Optional: Liefert eine Liste an Genres (falls verfügbar)."""
|
||
return []
|
||
|
||
def titles_for_genre(self, genre: str) -> List[str]:
|
||
"""Optional: Liefert alle Serientitel zu einem Genre."""
|
||
return []
|
||
|
||
def capabilities(self) -> Set[str]:
|
||
"""Optional: Liefert eine Menge an Features/Capabilities dieses Plugins.
|
||
|
||
Bekannte Werte:
|
||
- 'popular_series' – Plugin hat beliebte Serien/Filme
|
||
- 'latest_titles' – Plugin hat neu hinzugefuegte Titel
|
||
- 'year_filter' – Plugin unterstuetzt Jahr-Filter
|
||
- 'country_filter' – Plugin unterstuetzt Land-Filter
|
||
- 'collections' – Plugin hat Sammlungen/Filmreihen
|
||
- 'tags' – Plugin hat Tag/Schlagwort-Suche
|
||
- 'random' – Plugin kann einen zufaelligen Titel liefern
|
||
"""
|
||
|
||
return set()
|
||
|
||
def popular_series(self) -> List[str]:
|
||
"""Optional: Liefert eine Liste beliebter Serien (als Titel-Strings)."""
|
||
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Neue Felder fuer "Neue Titel"-Menü
|
||
# ------------------------------------------------------------------
|
||
|
||
def latest_titles(self, page: int = 1) -> List[str]:
|
||
"""Optional: Liefert neu hinzugefuegte Titel (Filme oder Serien).
|
||
|
||
Capability: 'latest_titles'
|
||
"""
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Jahr-Filter
|
||
# ------------------------------------------------------------------
|
||
|
||
def years_available(self) -> List[str]:
|
||
"""Optional: Liefert verfuegbare Erscheinungsjahre (z.B. ['2026', '2025', ...]).
|
||
|
||
Capability: 'year_filter'
|
||
"""
|
||
return []
|
||
|
||
def titles_for_year(self, year: str, page: int = 1) -> List[str]:
|
||
"""Optional: Liefert Titel fuer ein bestimmtes Erscheinungsjahr."""
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Land-Filter
|
||
# ------------------------------------------------------------------
|
||
|
||
def countries_available(self) -> List[str]:
|
||
"""Optional: Liefert verfuegbare Produktionslaender.
|
||
|
||
Capability: 'country_filter'
|
||
"""
|
||
return []
|
||
|
||
def titles_for_country(self, country: str, page: int = 1) -> List[str]:
|
||
"""Optional: Liefert Titel fuer ein bestimmtes Produktionsland."""
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Sammlungen / Collections
|
||
# ------------------------------------------------------------------
|
||
|
||
def collections(self) -> List[str]:
|
||
"""Optional: Liefert verfuegbare Sammlungen/Filmreihen.
|
||
|
||
Capability: 'collections'
|
||
"""
|
||
return []
|
||
|
||
def titles_for_collection(self, collection: str, page: int = 1) -> List[str]:
|
||
"""Optional: Liefert Titel einer Sammlung/Filmreihe."""
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Tags / Schlagworte
|
||
# ------------------------------------------------------------------
|
||
|
||
def tags(self) -> List[str]:
|
||
"""Optional: Liefert verfuegbare Schlagworte/Tags.
|
||
|
||
Capability: 'tags'
|
||
"""
|
||
return []
|
||
|
||
def titles_for_tag(self, tag: str, page: int = 1) -> List[str]:
|
||
"""Optional: Liefert Titel zu einem Schlagwort/Tag."""
|
||
return []
|
||
|
||
# ------------------------------------------------------------------
|
||
# Zufaelliger Titel
|
||
# ------------------------------------------------------------------
|
||
|
||
def random_title(self) -> Optional[str]:
|
||
"""Optional: Liefert einen zufaelligen Titel.
|
||
|
||
Capability: 'random'
|
||
"""
|
||
return None
|