Add Filmpalast series catalog browsing
This commit is contained in:
117
addon/default.py
117
addon/default.py
@@ -1021,6 +1021,9 @@ def _show_plugin_menu(plugin_name: str) -> None:
|
|||||||
if _plugin_has_capability(plugin, "alpha"):
|
if _plugin_has_capability(plugin, "alpha"):
|
||||||
_add_directory_item(handle, "A-Z", "alpha_index", {"plugin": plugin_name}, is_folder=True)
|
_add_directory_item(handle, "A-Z", "alpha_index", {"plugin": plugin_name}, is_folder=True)
|
||||||
|
|
||||||
|
if _plugin_has_capability(plugin, "series_catalog"):
|
||||||
|
_add_directory_item(handle, "Serien", "series_catalog", {"plugin": plugin_name, "page": "1"}, is_folder=True)
|
||||||
|
|
||||||
if _plugin_has_capability(plugin, "popular_series"):
|
if _plugin_has_capability(plugin, "popular_series"):
|
||||||
_add_directory_item(handle, "Meist gesehen", "popular", {"plugin": plugin_name, "page": "1"}, is_folder=True)
|
_add_directory_item(handle, "Meist gesehen", "popular", {"plugin": plugin_name, "page": "1"}, is_folder=True)
|
||||||
|
|
||||||
@@ -1875,6 +1878,115 @@ def _show_alpha_titles_page(plugin_name: str, letter: str, page: int = 1) -> Non
|
|||||||
xbmcplugin.endOfDirectory(handle)
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_series_catalog(plugin_name: str, page: int = 1) -> None:
|
||||||
|
handle = _get_handle()
|
||||||
|
plugin_name = (plugin_name or "").strip()
|
||||||
|
plugin = _discover_plugins().get(plugin_name)
|
||||||
|
if plugin is None:
|
||||||
|
xbmcgui.Dialog().notification("Serien", "Plugin nicht gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||||
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
return
|
||||||
|
|
||||||
|
page = max(1, int(page or 1))
|
||||||
|
paging_getter = getattr(plugin, "series_catalog_page", None)
|
||||||
|
if not callable(paging_getter):
|
||||||
|
xbmcgui.Dialog().notification("Serien", "Serien nicht verfügbar.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||||
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
return
|
||||||
|
|
||||||
|
total_pages = None
|
||||||
|
count_getter = getattr(plugin, "series_catalog_page_count", None)
|
||||||
|
if callable(count_getter):
|
||||||
|
try:
|
||||||
|
total_pages = int(count_getter(page) or 1)
|
||||||
|
except Exception:
|
||||||
|
total_pages = None
|
||||||
|
if total_pages is not None:
|
||||||
|
page = min(page, max(1, total_pages))
|
||||||
|
xbmcplugin.setPluginCategory(handle, f"Serien ({page}/{total_pages})")
|
||||||
|
else:
|
||||||
|
xbmcplugin.setPluginCategory(handle, f"Serien ({page})")
|
||||||
|
_set_content(handle, "tvshows")
|
||||||
|
|
||||||
|
if page > 1:
|
||||||
|
_add_directory_item(
|
||||||
|
handle,
|
||||||
|
"Vorherige Seite",
|
||||||
|
"series_catalog",
|
||||||
|
{"plugin": plugin_name, "page": str(page - 1)},
|
||||||
|
is_folder=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
titles = list(paging_getter(page) or [])
|
||||||
|
except Exception as exc:
|
||||||
|
_log(f"Serien konnten nicht geladen werden ({plugin_name} p{page}): {exc}", xbmc.LOGWARNING)
|
||||||
|
xbmcgui.Dialog().notification("Serien", "Serien konnten nicht geladen werden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||||
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
return
|
||||||
|
|
||||||
|
titles = [str(t).strip() for t in titles if t and str(t).strip()]
|
||||||
|
titles.sort(key=lambda value: value.casefold())
|
||||||
|
|
||||||
|
show_tmdb = _get_setting_bool("tmdb_genre_metadata", default=False)
|
||||||
|
if titles:
|
||||||
|
if show_tmdb:
|
||||||
|
with _busy_dialog():
|
||||||
|
tmdb_prefetched = _tmdb_labels_and_art_bulk(titles)
|
||||||
|
for title in titles:
|
||||||
|
info_labels, art, cast = tmdb_prefetched.get(title, _tmdb_labels_and_art(title))
|
||||||
|
info_labels = dict(info_labels or {})
|
||||||
|
info_labels.setdefault("mediatype", "tvshow")
|
||||||
|
if (info_labels.get("mediatype") or "").strip().casefold() == "tvshow":
|
||||||
|
info_labels.setdefault("tvshowtitle", title)
|
||||||
|
playstate = _title_playstate(plugin_name, title)
|
||||||
|
info_labels = _apply_playstate_to_info(dict(info_labels), playstate)
|
||||||
|
display_label = _label_with_duration(title, info_labels)
|
||||||
|
display_label = _label_with_playstate(display_label, playstate)
|
||||||
|
_add_directory_item(
|
||||||
|
handle,
|
||||||
|
display_label,
|
||||||
|
"seasons",
|
||||||
|
{"plugin": plugin_name, "title": title, **_series_url_params(plugin, title)},
|
||||||
|
is_folder=True,
|
||||||
|
info_labels=info_labels,
|
||||||
|
art=art,
|
||||||
|
cast=cast,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for title in titles:
|
||||||
|
playstate = _title_playstate(plugin_name, title)
|
||||||
|
_add_directory_item(
|
||||||
|
handle,
|
||||||
|
_label_with_playstate(title, playstate),
|
||||||
|
"seasons",
|
||||||
|
{"plugin": plugin_name, "title": title, **_series_url_params(plugin, title)},
|
||||||
|
is_folder=True,
|
||||||
|
info_labels=_apply_playstate_to_info({"title": title}, playstate),
|
||||||
|
)
|
||||||
|
|
||||||
|
show_next = False
|
||||||
|
if total_pages is not None:
|
||||||
|
show_next = page < total_pages
|
||||||
|
else:
|
||||||
|
has_more_getter = getattr(plugin, "series_catalog_has_more", None)
|
||||||
|
if callable(has_more_getter):
|
||||||
|
try:
|
||||||
|
show_next = bool(has_more_getter(page))
|
||||||
|
except Exception:
|
||||||
|
show_next = False
|
||||||
|
|
||||||
|
if show_next:
|
||||||
|
_add_directory_item(
|
||||||
|
handle,
|
||||||
|
"Nächste Seite",
|
||||||
|
"series_catalog",
|
||||||
|
{"plugin": plugin_name, "page": str(page + 1)},
|
||||||
|
is_folder=True,
|
||||||
|
)
|
||||||
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
|
||||||
|
|
||||||
def _title_group_key(title: str) -> str:
|
def _title_group_key(title: str) -> str:
|
||||||
raw = (title or "").strip()
|
raw = (title or "").strip()
|
||||||
if not raw:
|
if not raw:
|
||||||
@@ -2862,6 +2974,11 @@ def run() -> None:
|
|||||||
params.get("letter", ""),
|
params.get("letter", ""),
|
||||||
_parse_positive_int(params.get("page", "1"), default=1),
|
_parse_positive_int(params.get("page", "1"), default=1),
|
||||||
)
|
)
|
||||||
|
elif action == "series_catalog":
|
||||||
|
_show_series_catalog(
|
||||||
|
params.get("plugin", ""),
|
||||||
|
_parse_positive_int(params.get("page", "1"), default=1),
|
||||||
|
)
|
||||||
elif action == "genre_series_group":
|
elif action == "genre_series_group":
|
||||||
_show_genre_series_group(
|
_show_genre_series_group(
|
||||||
params.get("plugin", ""),
|
params.get("plugin", ""),
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ DEFAULT_BASE_URL = "https://filmpalast.to"
|
|||||||
DEFAULT_TIMEOUT = 20
|
DEFAULT_TIMEOUT = 20
|
||||||
DEFAULT_PREFERRED_HOSTERS = ["voe", "vidoza", "streamtape", "doodstream", "mixdrop"]
|
DEFAULT_PREFERRED_HOSTERS = ["voe", "vidoza", "streamtape", "doodstream", "mixdrop"]
|
||||||
SERIES_HINT_PREFIX = "series://filmpalast/"
|
SERIES_HINT_PREFIX = "series://filmpalast/"
|
||||||
|
SERIES_VIEW_PATH = "/serien/view"
|
||||||
SEASON_EPISODE_RE = re.compile(r"\bS\s*(\d{1,2})\s*E\s*(\d{1,3})\b", re.IGNORECASE)
|
SEASON_EPISODE_RE = re.compile(r"\bS\s*(\d{1,2})\s*E\s*(\d{1,3})\b", re.IGNORECASE)
|
||||||
GLOBAL_SETTING_LOG_URLS = "debug_log_urls"
|
GLOBAL_SETTING_LOG_URLS = "debug_log_urls"
|
||||||
GLOBAL_SETTING_DUMP_HTML = "debug_dump_html"
|
GLOBAL_SETTING_DUMP_HTML = "debug_dump_html"
|
||||||
@@ -229,6 +230,7 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
self._genre_page_count_cache: Dict[str, int] = {}
|
self._genre_page_count_cache: Dict[str, int] = {}
|
||||||
self._alpha_to_url: Dict[str, str] = {}
|
self._alpha_to_url: Dict[str, str] = {}
|
||||||
self._alpha_page_count_cache: Dict[str, int] = {}
|
self._alpha_page_count_cache: Dict[str, int] = {}
|
||||||
|
self._series_page_count_cache: Dict[int, int] = {}
|
||||||
self._requests_available = REQUESTS_AVAILABLE
|
self._requests_available = REQUESTS_AVAILABLE
|
||||||
self._default_preferred_hosters: List[str] = list(DEFAULT_PREFERRED_HOSTERS)
|
self._default_preferred_hosters: List[str] = list(DEFAULT_PREFERRED_HOSTERS)
|
||||||
self._preferred_hosters: List[str] = list(self._default_preferred_hosters)
|
self._preferred_hosters: List[str] = list(self._default_preferred_hosters)
|
||||||
@@ -497,7 +499,7 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
return max_page
|
return max_page
|
||||||
|
|
||||||
def capabilities(self) -> set[str]:
|
def capabilities(self) -> set[str]:
|
||||||
return {"genres", "alpha"}
|
return {"genres", "alpha", "series_catalog"}
|
||||||
|
|
||||||
def _parse_alpha_links(self, soup: BeautifulSoupT) -> Dict[str, str]:
|
def _parse_alpha_links(self, soup: BeautifulSoupT) -> Dict[str, str]:
|
||||||
alpha: Dict[str, str] = {}
|
alpha: Dict[str, str] = {}
|
||||||
@@ -571,6 +573,45 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
titles.sort(key=lambda value: value.casefold())
|
titles.sort(key=lambda value: value.casefold())
|
||||||
return titles
|
return titles
|
||||||
|
|
||||||
|
def _series_view_url(self) -> str:
|
||||||
|
return _absolute_url(SERIES_VIEW_PATH)
|
||||||
|
|
||||||
|
def series_catalog_page_count(self, page: int = 1) -> int:
|
||||||
|
if not self._requests_available:
|
||||||
|
return 1
|
||||||
|
cache_key = int(page or 1)
|
||||||
|
if cache_key in self._series_page_count_cache:
|
||||||
|
return max(1, int(self._series_page_count_cache.get(cache_key, 1)))
|
||||||
|
base_url = self._series_view_url()
|
||||||
|
if not base_url:
|
||||||
|
return 1
|
||||||
|
try:
|
||||||
|
soup = _get_soup(base_url, session=get_requests_session("filmpalast", headers=HEADERS))
|
||||||
|
except Exception:
|
||||||
|
return 1
|
||||||
|
pages = self._extract_last_page(soup)
|
||||||
|
self._series_page_count_cache[cache_key] = max(1, pages)
|
||||||
|
return self._series_page_count_cache[cache_key]
|
||||||
|
|
||||||
|
def series_catalog_page(self, page: int) -> List[str]:
|
||||||
|
if not self._requests_available:
|
||||||
|
return []
|
||||||
|
base_url = self._series_view_url()
|
||||||
|
if not base_url:
|
||||||
|
return []
|
||||||
|
page = max(1, int(page or 1))
|
||||||
|
url = base_url if page == 1 else urljoin(base_url.rstrip("/") + "/", f"page/{page}")
|
||||||
|
try:
|
||||||
|
soup = _get_soup(url, session=get_requests_session("filmpalast", headers=HEADERS))
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
hits = self._parse_listing_hits(soup)
|
||||||
|
return self._apply_hits_to_title_index(hits)
|
||||||
|
|
||||||
|
def series_catalog_has_more(self, page: int) -> bool:
|
||||||
|
total = self.series_catalog_page_count(page)
|
||||||
|
return page < total
|
||||||
|
|
||||||
def genres(self) -> List[str]:
|
def genres(self) -> List[str]:
|
||||||
if not self._requests_available:
|
if not self._requests_available:
|
||||||
return []
|
return []
|
||||||
|
|||||||
Reference in New Issue
Block a user