dev: bump to 0.1.74-dev – BurningSeries entfernt, Paging-Fix Neuste Titel
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<addon id="plugin.video.viewit" name="ViewIt" version="0.1.73-dev" provider-name="ViewIt">
|
||||
<addon id="plugin.video.viewit" name="ViewIt" version="0.1.74-dev" provider-name="ViewIt">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="3.0.0" />
|
||||
<import addon="script.module.requests" />
|
||||
|
||||
261
addon/default.py
261
addon/default.py
@@ -1203,6 +1203,7 @@ def _normalize_update_info_url(raw: str) -> str:
|
||||
UPDATE_CHANNEL_MAIN = 0
|
||||
UPDATE_CHANNEL_NIGHTLY = 1
|
||||
UPDATE_CHANNEL_CUSTOM = 2
|
||||
UPDATE_CHANNEL_DEV = 3
|
||||
_AUTO_UPDATE_INTERVALS = [1 * 60 * 60, 6 * 60 * 60, 24 * 60 * 60] # 1h, 6h, 24h
|
||||
UPDATE_HTTP_TIMEOUT_SEC = 8
|
||||
UPDATE_ADDON_ID = "plugin.video.viewit"
|
||||
@@ -1212,7 +1213,7 @@ RESOLVEURL_AUTO_INSTALL_INTERVAL_SEC = 6 * 60 * 60
|
||||
|
||||
def _selected_update_channel() -> int:
|
||||
channel = _get_setting_int("update_channel", default=UPDATE_CHANNEL_MAIN)
|
||||
if channel not in {UPDATE_CHANNEL_MAIN, UPDATE_CHANNEL_NIGHTLY, UPDATE_CHANNEL_CUSTOM}:
|
||||
if channel not in {UPDATE_CHANNEL_MAIN, UPDATE_CHANNEL_NIGHTLY, UPDATE_CHANNEL_CUSTOM, UPDATE_CHANNEL_DEV}:
|
||||
return UPDATE_CHANNEL_MAIN
|
||||
return channel
|
||||
|
||||
@@ -1222,6 +1223,8 @@ def _channel_label(channel: int) -> str:
|
||||
return "Nightly"
|
||||
if channel == UPDATE_CHANNEL_CUSTOM:
|
||||
return "Custom"
|
||||
if channel == UPDATE_CHANNEL_DEV:
|
||||
return "Dev"
|
||||
return "Main"
|
||||
|
||||
|
||||
@@ -1246,11 +1249,17 @@ def _is_nightly_version(version: str) -> bool:
|
||||
return bool(re.match(r"^\d+\.\d+\.\d+-nightly$", str(version or "").strip()))
|
||||
|
||||
|
||||
def _is_dev_version(version: str) -> bool:
|
||||
return bool(re.match(r"^\d+\.\d+\.\d+-dev$", str(version or "").strip()))
|
||||
|
||||
|
||||
def _filter_versions_for_channel(channel: int, versions: list[str]) -> list[str]:
|
||||
if channel == UPDATE_CHANNEL_MAIN:
|
||||
return [v for v in versions if _is_stable_version(v)]
|
||||
if channel == UPDATE_CHANNEL_NIGHTLY:
|
||||
return [v for v in versions if _is_nightly_version(v)]
|
||||
if channel == UPDATE_CHANNEL_DEV:
|
||||
return [v for v in versions if _is_dev_version(v)]
|
||||
return list(versions)
|
||||
|
||||
|
||||
@@ -1260,6 +1269,8 @@ def _resolve_update_info_url() -> str:
|
||||
raw = _get_setting_string("update_repo_url_nightly")
|
||||
elif channel == UPDATE_CHANNEL_CUSTOM:
|
||||
raw = _get_setting_string("update_repo_url")
|
||||
elif channel == UPDATE_CHANNEL_DEV:
|
||||
raw = _get_setting_string("update_repo_url_dev")
|
||||
else:
|
||||
raw = _get_setting_string("update_repo_url_main")
|
||||
return _normalize_update_info_url(raw)
|
||||
@@ -1661,24 +1672,6 @@ def _show_root_menu() -> None:
|
||||
for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()):
|
||||
_add_directory_item(handle, plugin_name, "plugin_menu", {"plugin": plugin_name}, is_folder=True)
|
||||
|
||||
# Katalog-Caches im Hintergrund vorwaermen (fire-and-forget)
|
||||
for _pname, _plugin in plugins.items():
|
||||
_warmer = getattr(_plugin, "warm_catalog_cache", None)
|
||||
if callable(_warmer):
|
||||
def _warm_and_notify(_fn=_warmer, _name=_pname):
|
||||
try:
|
||||
loaded = _fn()
|
||||
except Exception:
|
||||
loaded = False
|
||||
if loaded:
|
||||
# executebuiltin ist thread-sicher; xbmcgui.Dialog() darf nicht
|
||||
# aus Daemon-Threads aufgerufen werden (Kodi-Absturzgefahr).
|
||||
safe_name = _name.replace('"', "")
|
||||
xbmc.executebuiltin(
|
||||
f'Notification("{safe_name}", "Suchindex geladen", 3000, "")'
|
||||
)
|
||||
threading.Thread(target=_warm_and_notify, daemon=True, name=f"viewit-warmup-{_pname}").start()
|
||||
|
||||
# Trakt-Menue (nur wenn aktiviert)
|
||||
if _get_setting_bool("trakt_enabled", default=False):
|
||||
if _trakt_load_token():
|
||||
@@ -1704,7 +1697,8 @@ def _show_plugin_menu(plugin_name: str) -> None:
|
||||
|
||||
xbmcplugin.setPluginCategory(handle, plugin_name)
|
||||
|
||||
_add_directory_item(handle, "Suche", "plugin_search", {"plugin": plugin_name}, is_folder=True)
|
||||
if callable(getattr(plugin, "search_titles", None)):
|
||||
_add_directory_item(handle, "Suche", "plugin_search", {"plugin": plugin_name}, is_folder=True)
|
||||
|
||||
if _plugin_has_capability(plugin, "new_titles") or _plugin_has_capability(plugin, "latest_episodes"):
|
||||
_add_directory_item(handle, LATEST_MENU_LABEL, "latest_titles", {"plugin": plugin_name, "page": "1"}, is_folder=True)
|
||||
@@ -2527,34 +2521,6 @@ def _show_episodes(plugin_name: str, title: str, season: str, series_url: str =
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_genre_sources() -> None:
|
||||
handle = _get_handle()
|
||||
_log("Genre-Quellen laden.")
|
||||
plugins = _discover_plugins()
|
||||
sources: list[tuple[str, BasisPlugin]] = []
|
||||
for plugin_name, plugin in plugins.items():
|
||||
if plugin.__class__.genres is BasisPlugin.genres:
|
||||
continue
|
||||
if plugin.__class__.titles_for_genre is BasisPlugin.titles_for_genre:
|
||||
continue
|
||||
sources.append((plugin_name, plugin))
|
||||
|
||||
if not sources:
|
||||
xbmcgui.Dialog().notification("Genres", "Keine Quellen mit Genres gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
for plugin_name, plugin in sources:
|
||||
_add_directory_item(
|
||||
handle,
|
||||
f"Genres [{plugin_name}]",
|
||||
"genres",
|
||||
{"plugin": plugin_name},
|
||||
is_folder=True,
|
||||
)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_genres(plugin_name: str) -> None:
|
||||
handle = _get_handle()
|
||||
_log(f"Genres laden: {plugin_name}")
|
||||
@@ -2596,44 +2562,6 @@ def _show_genres(plugin_name: str) -> None:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_categories(plugin_name: str) -> None:
|
||||
handle = _get_handle()
|
||||
_log(f"Kategorien laden: {plugin_name}")
|
||||
plugin = _discover_plugins().get(plugin_name)
|
||||
if plugin is None:
|
||||
xbmcgui.Dialog().notification("Kategorien", "Quelle nicht gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
getter = getattr(plugin, "categories", None)
|
||||
if not callable(getter):
|
||||
xbmcgui.Dialog().notification("Kategorien", "Kategorien nicht verfuegbar.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
try:
|
||||
categories = _run_with_progress(
|
||||
"Kategorien",
|
||||
f"{plugin_name}: Kategorien werden geladen...",
|
||||
lambda: list(getter() or []),
|
||||
)
|
||||
except Exception as exc:
|
||||
_log(f"Kategorien konnten nicht geladen werden ({plugin_name}): {exc}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification("Kategorien", "Kategorien konnten nicht geladen werden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
for category in categories:
|
||||
category = str(category).strip()
|
||||
if not category:
|
||||
continue
|
||||
_add_directory_item(
|
||||
handle,
|
||||
category,
|
||||
"category_titles_page",
|
||||
{"plugin": plugin_name, "category": category, "page": "1"},
|
||||
is_folder=True,
|
||||
)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_paged_title_list(
|
||||
plugin_name: str,
|
||||
filter_value: str,
|
||||
@@ -2767,18 +2695,6 @@ def _show_paged_title_list(
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_category_titles_page(plugin_name: str, category: str, page: int = 1) -> None:
|
||||
_show_paged_title_list(
|
||||
plugin_name, category, page,
|
||||
dialog_label="Kategorien",
|
||||
page_action="category_titles_page",
|
||||
filter_param="category",
|
||||
paging_method="titles_for_genre_page",
|
||||
count_method="genre_page_count",
|
||||
has_more_method="genre_has_more",
|
||||
)
|
||||
|
||||
|
||||
def _show_genre_titles_page(plugin_name: str, genre: str, page: int = 1) -> None:
|
||||
_show_paged_title_list(
|
||||
plugin_name, genre, page,
|
||||
@@ -3351,6 +3267,8 @@ def _show_new_titles(plugin_name: str, page: int = 1, *, action_name: str = "new
|
||||
show_next = bool(has_more_getter(page))
|
||||
except Exception:
|
||||
show_next = False
|
||||
elif callable(paging_getter) and page_items:
|
||||
show_next = True
|
||||
elif "total_pages" in locals():
|
||||
show_next = bool(total_pages > 1 and page < total_pages) # type: ignore[name-defined]
|
||||
|
||||
@@ -3445,7 +3363,12 @@ def _show_latest_episodes(plugin_name: str, page: int = 1) -> None:
|
||||
info_labels=info_labels,
|
||||
)
|
||||
|
||||
if entries:
|
||||
has_more_fn = getattr(plugin, "latest_episodes_has_more", None)
|
||||
if callable(has_more_fn):
|
||||
show_next = bool(has_more_fn(page))
|
||||
else:
|
||||
show_next = False
|
||||
if show_next:
|
||||
_add_directory_item(handle, "Naechste Seite", "latest_titles",
|
||||
{"plugin": plugin_name, "page": str(page + 1)}, is_folder=True)
|
||||
|
||||
@@ -3842,6 +3765,51 @@ def _is_resolveurl_missing_error(message: str) -> bool:
|
||||
return str(message or "").strip().casefold() == "resolveurl missing"
|
||||
|
||||
|
||||
def _resolve_stream_with_retry(plugin: BasisPlugin, link: str) -> str | None:
|
||||
"""Löst einen Stream-Link auf mit ResolveURL-Auto-Install und CF-Check.
|
||||
|
||||
Gibt den finalen Link zurück oder None (mit Kodi-Notification bei Fehler).
|
||||
"""
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
if not resolved_link:
|
||||
err = _resolveurl_last_error()
|
||||
if _is_resolveurl_missing_error(err):
|
||||
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
||||
_ensure_resolveurl_installed(force=True, silent=True)
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err):
|
||||
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
return None
|
||||
if not resolved_link:
|
||||
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Stream konnte nicht aufgeloest werden.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
3000,
|
||||
)
|
||||
return None
|
||||
final_link = normalize_resolved_stream_url(resolved_link, source_url=link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err) and final_link.strip() == link.strip():
|
||||
_log(f"ResolveURL Cloudflare-Challenge (unresolved): {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
return None
|
||||
return final_link
|
||||
|
||||
|
||||
def _play_final_link(
|
||||
link: str,
|
||||
*,
|
||||
@@ -4086,43 +4054,8 @@ def _play_episode(
|
||||
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
return
|
||||
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
if not resolved_link:
|
||||
err = _resolveurl_last_error()
|
||||
if _is_resolveurl_missing_error(err):
|
||||
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
||||
_ensure_resolveurl_installed(force=True, silent=True)
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err):
|
||||
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
return
|
||||
if not resolved_link:
|
||||
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Stream konnte nicht aufgeloest werden.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
3000,
|
||||
)
|
||||
return
|
||||
final_link = resolved_link
|
||||
final_link = normalize_resolved_stream_url(final_link, source_url=link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err) and final_link.strip() == link.strip():
|
||||
_log(f"ResolveURL Cloudflare-Challenge (unresolved): {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
final_link = _resolve_stream_with_retry(plugin, link)
|
||||
if not final_link:
|
||||
return
|
||||
finally:
|
||||
if restore_hosters is not None and callable(preferred_setter):
|
||||
@@ -4237,43 +4170,8 @@ def _play_episode_url(
|
||||
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
return
|
||||
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
if not resolved_link:
|
||||
err = _resolveurl_last_error()
|
||||
if _is_resolveurl_missing_error(err):
|
||||
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
||||
_ensure_resolveurl_installed(force=True, silent=True)
|
||||
resolved_link = plugin.resolve_stream_link(link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err):
|
||||
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
return
|
||||
if not resolved_link:
|
||||
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Stream konnte nicht aufgeloest werden.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
3000,
|
||||
)
|
||||
return
|
||||
final_link = resolved_link
|
||||
final_link = normalize_resolved_stream_url(final_link, source_url=link)
|
||||
err = _resolveurl_last_error()
|
||||
if _is_cloudflare_challenge_error(err) and final_link.strip() == link.strip():
|
||||
_log(f"ResolveURL Cloudflare-Challenge (unresolved): {err}", xbmc.LOGWARNING)
|
||||
xbmcgui.Dialog().notification(
|
||||
"Wiedergabe",
|
||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||
xbmcgui.NOTIFICATION_INFO,
|
||||
4500,
|
||||
)
|
||||
final_link = _resolve_stream_with_retry(plugin, link)
|
||||
if not final_link:
|
||||
return
|
||||
finally:
|
||||
if restore_hosters is not None and callable(preferred_setter):
|
||||
@@ -4945,21 +4843,11 @@ def _route_plugin_search(params: dict[str, str]) -> None:
|
||||
_show_plugin_search(params.get("plugin", ""))
|
||||
|
||||
|
||||
@_router.route("genre_sources")
|
||||
def _route_genre_sources(params: dict[str, str]) -> None:
|
||||
_show_genre_sources()
|
||||
|
||||
|
||||
@_router.route("genres")
|
||||
def _route_genres(params: dict[str, str]) -> None:
|
||||
_show_genres(params.get("plugin", ""))
|
||||
|
||||
|
||||
@_router.route("categories")
|
||||
def _route_categories(params: dict[str, str]) -> None:
|
||||
_show_categories(params.get("plugin", ""))
|
||||
|
||||
|
||||
@_router.route("latest_titles")
|
||||
def _route_latest_titles(params: dict[str, str]) -> None:
|
||||
_show_latest_titles(params.get("plugin", ""), _parse_positive_int(params.get("page", "1"), default=1))
|
||||
@@ -4988,13 +4876,6 @@ def _route_genre_titles_page(params: dict[str, str]) -> None:
|
||||
)
|
||||
|
||||
|
||||
@_router.route("category_titles_page")
|
||||
def _route_category_titles_page(params: dict[str, str]) -> None:
|
||||
_show_category_titles_page(
|
||||
params.get("plugin", ""), params.get("category", ""),
|
||||
_parse_positive_int(params.get("page", "1"), default=1),
|
||||
)
|
||||
|
||||
|
||||
@_router.route("alpha_index")
|
||||
def _route_alpha_index(params: dict[str, str]) -> None:
|
||||
|
||||
@@ -9,7 +9,7 @@ Vorgehen fuer ein neues Plugin:
|
||||
3. `search_titles`, `seasons_for`, `episodes_for` gemaess Zielseite implementieren
|
||||
4. Optional weitere Methoden implementieren – capabilities deklarieren und Methoden ueberschreiben:
|
||||
- `popular_series()` + capability 'popular_series'
|
||||
- `latest_titles(page)` + capability 'latest_titles'
|
||||
- `new_titles()` + `new_titles_page(page)` + capability 'new_titles'
|
||||
- `genres()` + `titles_for_genre(genre)` + `titles_for_genre_page(genre, page)`
|
||||
- `alpha_index()` + `titles_for_alpha_page(letter, page)`
|
||||
- `years_available()` + `titles_for_year(year, page)` + capability 'year_filter'
|
||||
@@ -169,7 +169,7 @@ class TemplatePlugin(BasisPlugin):
|
||||
|
||||
Bekannte Werte (aus plugin_interface.py):
|
||||
- 'popular_series' – Plugin hat beliebte Serien/Filme
|
||||
- 'latest_titles' – Plugin hat neu hinzugefuegte Titel
|
||||
- 'new_titles' – Plugin hat neu hinzugefuegte Titel
|
||||
- 'year_filter' – Plugin unterstuetzt Jahr-Filter
|
||||
- 'country_filter' – Plugin unterstuetzt Land-Filter
|
||||
- 'collections' – Plugin hat Sammlungen/Filmreihen
|
||||
|
||||
@@ -1025,7 +1025,7 @@ class AniworldPlugin(BasisPlugin):
|
||||
_session_cache_set(self._season_episodes_cache_name(season_url), payload)
|
||||
|
||||
def capabilities(self) -> set[str]:
|
||||
return {"popular_series", "genres", "latest_episodes", "latest_titles"}
|
||||
return {"popular_series", "genres", "latest_episodes", "new_titles"}
|
||||
|
||||
def _find_series_by_title(self, title: str) -> Optional[SeriesResult]:
|
||||
title = (title or "").strip()
|
||||
@@ -1345,7 +1345,7 @@ class AniworldPlugin(BasisPlugin):
|
||||
except Exception:
|
||||
return 1
|
||||
|
||||
def latest_titles(self, page: int = 1) -> List[str]:
|
||||
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||
"""Liefert neu hinzugefuegte Anime vom Animekalender."""
|
||||
if not self._requests_available:
|
||||
return []
|
||||
@@ -1372,6 +1372,9 @@ class AniworldPlugin(BasisPlugin):
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def new_titles(self) -> List[str]:
|
||||
return self.new_titles_page(1)
|
||||
|
||||
def _season_label(self, number: int) -> str:
|
||||
return f"Staffel {number}"
|
||||
|
||||
|
||||
@@ -1019,7 +1019,7 @@ class EinschaltenPlugin(BasisPlugin):
|
||||
return resolve_via_resolveurl(link, fallback_to_link=True)
|
||||
|
||||
def capabilities(self) -> Set[str]:
|
||||
return {"new_titles", "genres", "popular_series", "latest_titles"}
|
||||
return {"new_titles", "genres", "popular_series"}
|
||||
|
||||
def popular_series(self) -> List[str]:
|
||||
"""Liefert die am besten bewerteten Filme (nach voteAverage sortiert)."""
|
||||
@@ -1042,14 +1042,6 @@ class EinschaltenPlugin(BasisPlugin):
|
||||
titles.append(movie.title)
|
||||
return titles
|
||||
|
||||
def latest_titles(self, page: int = 1) -> List[str]:
|
||||
"""Liefert neu hinzugefügte Filme (Alias zu new_titles_page)."""
|
||||
if not REQUESTS_AVAILABLE:
|
||||
return []
|
||||
if not self._get_base_url():
|
||||
return []
|
||||
return self.new_titles_page(max(1, int(page or 1)))
|
||||
|
||||
def new_titles(self) -> List[str]:
|
||||
if not REQUESTS_AVAILABLE:
|
||||
return []
|
||||
|
||||
@@ -525,7 +525,7 @@ class FilmpalastPlugin(BasisPlugin):
|
||||
return max_page
|
||||
|
||||
def capabilities(self) -> set[str]:
|
||||
return {"genres", "alpha", "series_catalog", "popular_series", "latest_titles"}
|
||||
return {"genres", "alpha", "series_catalog", "popular_series", "new_titles"}
|
||||
|
||||
def _parse_alpha_links(self, soup: BeautifulSoupT) -> Dict[str, str]:
|
||||
alpha: Dict[str, str] = {}
|
||||
@@ -1057,7 +1057,7 @@ class FilmpalastPlugin(BasisPlugin):
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def latest_titles(self, page: int = 1) -> List[str]:
|
||||
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||
"""Liefert neu hinzugefuegte Titel von /movies/new."""
|
||||
if not self._requests_available:
|
||||
return []
|
||||
@@ -1071,6 +1071,9 @@ class FilmpalastPlugin(BasisPlugin):
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def new_titles(self) -> List[str]:
|
||||
return self.new_titles_page(1)
|
||||
|
||||
def resolve_stream_link(self, link: str) -> Optional[str]:
|
||||
if not link:
|
||||
return None
|
||||
|
||||
@@ -231,10 +231,13 @@ class NetzkinoPlugin(BasisPlugin):
|
||||
# Browsing
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def latest_titles(self, page: int = 1) -> List[str]:
|
||||
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||
url = _URL_CATEGORY.format(slug="neue-filme")
|
||||
return self._load_posts(url)
|
||||
|
||||
def new_titles(self) -> List[str]:
|
||||
return self.new_titles_page(1)
|
||||
|
||||
def genres(self) -> List[str]:
|
||||
# Gibt die Anzeigenamen zurück (sortiert, Browsing-Kategorien)
|
||||
return sorted(CATEGORIES.values())
|
||||
@@ -248,4 +251,4 @@ class NetzkinoPlugin(BasisPlugin):
|
||||
return self._load_posts(url)
|
||||
|
||||
def capabilities(self) -> set[str]:
|
||||
return {"latest_titles", "genres"}
|
||||
return {"new_titles", "genres"}
|
||||
|
||||
@@ -1167,7 +1167,7 @@ class TopstreamfilmPlugin(BasisPlugin):
|
||||
return resolve_via_resolveurl(link, fallback_to_link=True)
|
||||
|
||||
def capabilities(self) -> set[str]:
|
||||
return {"genres", "popular_series", "year_filter", "latest_titles"}
|
||||
return {"genres", "popular_series", "year_filter", "new_titles"}
|
||||
|
||||
def years_available(self) -> List[str]:
|
||||
"""Liefert verfügbare Erscheinungsjahre (aktuelles Jahr bis 1980)."""
|
||||
@@ -1207,7 +1207,7 @@ class TopstreamfilmPlugin(BasisPlugin):
|
||||
self._save_title_url_cache()
|
||||
return titles
|
||||
|
||||
def latest_titles(self, page: int = 1) -> List[str]:
|
||||
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||
"""Liefert neu hinzugefügte Filme.
|
||||
|
||||
URL-Muster: /neueste-filme/ oder /neueste-filme/page/{n}/
|
||||
@@ -1238,6 +1238,9 @@ class TopstreamfilmPlugin(BasisPlugin):
|
||||
self._save_title_url_cache()
|
||||
return titles
|
||||
|
||||
def new_titles(self) -> List[str]:
|
||||
return self.new_titles_page(1)
|
||||
|
||||
|
||||
# Alias für die automatische Plugin-Erkennung.
|
||||
Plugin = TopstreamfilmPlugin
|
||||
|
||||
Reference in New Issue
Block a user