128 lines
3.9 KiB
Python
128 lines
3.9 KiB
Python
"""Template fuer ein neues ViewIt-Plugin (Basis: serienstream_plugin).
|
|
|
|
Diese Datei wird NICHT automatisch geladen (Dateiname beginnt mit `_`).
|
|
Zum Verwenden:
|
|
1) Kopiere/benenne die Datei um (ohne fuehrenden Unterstrich), z.B. `my_site_plugin.py`
|
|
2) Passe `name`, `BASE_URL` und die Implementierungen an.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Any, List, Optional
|
|
|
|
try: # pragma: no cover - optional dependency
|
|
import requests
|
|
from bs4 import BeautifulSoup # type: ignore[import-not-found]
|
|
except ImportError as exc: # pragma: no cover - optional dependency
|
|
requests = None
|
|
BeautifulSoup = None
|
|
REQUESTS_AVAILABLE = False
|
|
REQUESTS_IMPORT_ERROR = exc
|
|
else:
|
|
REQUESTS_AVAILABLE = True
|
|
REQUESTS_IMPORT_ERROR = None
|
|
|
|
try: # pragma: no cover - optional Kodi helpers
|
|
import xbmcaddon # type: ignore[import-not-found]
|
|
except ImportError: # pragma: no cover - allow running outside Kodi
|
|
xbmcaddon = None
|
|
|
|
from plugin_interface import BasisPlugin
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
from requests import Session as RequestsSession
|
|
from bs4 import BeautifulSoup as BeautifulSoupT # type: ignore[import-not-found]
|
|
else: # pragma: no cover
|
|
RequestsSession = Any
|
|
BeautifulSoupT = Any
|
|
|
|
|
|
ADDON_ID = "plugin.video.viewit"
|
|
BASE_URL = "https://example.com"
|
|
DEFAULT_TIMEOUT = 20
|
|
HEADERS = {
|
|
"User-Agent": "Mozilla/5.0 (Kodi; ViewIt) AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
"Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
|
|
"Connection": "keep-alive",
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TitleHit:
|
|
"""Ein Suchtreffer mit Titel und Detail-URL."""
|
|
|
|
title: str
|
|
url: str
|
|
|
|
|
|
class TemplatePlugin(BasisPlugin):
|
|
"""Vorlage fuer eine Streamingseiten-Integration.
|
|
|
|
Optional kann ein Plugin Capabilities deklarieren (z.B. `popular_series`),
|
|
damit der Router passende Menüpunkte anbieten kann.
|
|
"""
|
|
|
|
name = "Template"
|
|
|
|
def __init__(self) -> None:
|
|
self._session: RequestsSession | None = None
|
|
|
|
@property
|
|
def is_available(self) -> bool:
|
|
return REQUESTS_AVAILABLE
|
|
|
|
@property
|
|
def unavailable_reason(self) -> str:
|
|
if REQUESTS_AVAILABLE:
|
|
return ""
|
|
return f"requests/bs4 nicht verfuegbar: {REQUESTS_IMPORT_ERROR}"
|
|
|
|
def _get_session(self) -> RequestsSession:
|
|
if requests is None:
|
|
raise RuntimeError(self.unavailable_reason)
|
|
if self._session is None:
|
|
session = requests.Session()
|
|
session.headers.update(HEADERS)
|
|
self._session = session
|
|
return self._session
|
|
|
|
async def search_titles(self, query: str) -> List[str]:
|
|
"""TODO: Suche auf der Zielseite implementieren."""
|
|
_ = query
|
|
return []
|
|
|
|
def seasons_for(self, title: str) -> List[str]:
|
|
"""TODO: Staffeln fuer einen Titel liefern."""
|
|
_ = title
|
|
return []
|
|
|
|
def episodes_for(self, title: str, season: str) -> List[str]:
|
|
"""TODO: Episoden fuer Titel+Staffel liefern."""
|
|
_ = (title, season)
|
|
return []
|
|
|
|
def capabilities(self) -> set[str]:
|
|
"""Optional: Deklariert Fähigkeiten dieses Plugins.
|
|
|
|
Beispiele:
|
|
- `popular_series`: Plugin kann beliebte Serien liefern
|
|
- `genres`: Plugin unterstützt Genre-Browser
|
|
"""
|
|
|
|
return set()
|
|
|
|
def popular_series(self) -> List[str]:
|
|
"""Optional: Liste beliebter Serien (nur wenn `popular_series` gesetzt ist)."""
|
|
return []
|
|
|
|
def stream_link_for(self, title: str, season: str, episode: str) -> Optional[str]:
|
|
"""Optional: Embed-/Hoster-Link fuer eine Episode."""
|
|
_ = (title, season, episode)
|
|
return None
|
|
|
|
def resolve_stream_link(self, link: str) -> Optional[str]:
|
|
"""Optional: Redirect-/Mirror-Aufloesung."""
|
|
return link
|