- Added comprehensive documentation for the ViewIt Plugin System, detailing the plugin loading process, required methods, optional features, and community extension workflow. - Updated project notes to reflect the current structure, build process, search logic, and known issues. - Introduced new build scripts for installing the add-on and creating ZIP packages. - Added test scripts for TMDB API integration, including argument parsing and logging functionality. - Enhanced existing plugins with improved search logic and error handling.
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Shared helpers for ViewIt plugins.
|
|
|
|
Focus:
|
|
- Kodi addon settings access (string/bool)
|
|
- Optional URL notifications
|
|
- Optional URL logging
|
|
- Optional HTML response dumps
|
|
|
|
Designed to work both in Kodi and outside Kodi (for linting/tests).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
import hashlib
|
|
import os
|
|
from typing import Optional
|
|
|
|
try: # pragma: no cover - Kodi runtime
|
|
import xbmcaddon # type: ignore[import-not-found]
|
|
import xbmcvfs # type: ignore[import-not-found]
|
|
import xbmcgui # type: ignore[import-not-found]
|
|
except ImportError: # pragma: no cover - allow importing outside Kodi
|
|
xbmcaddon = None
|
|
xbmcvfs = None
|
|
xbmcgui = None
|
|
|
|
|
|
def get_setting_string(addon_id: str, setting_id: str, *, default: str = "") -> str:
|
|
if xbmcaddon is None:
|
|
return default
|
|
try:
|
|
addon = xbmcaddon.Addon(addon_id)
|
|
getter = getattr(addon, "getSettingString", None)
|
|
if getter is not None:
|
|
return str(getter(setting_id) or "").strip()
|
|
return str(addon.getSetting(setting_id) or "").strip()
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
def get_setting_bool(addon_id: str, setting_id: str, *, default: bool = False) -> bool:
|
|
if xbmcaddon is None:
|
|
return default
|
|
try:
|
|
addon = xbmcaddon.Addon(addon_id)
|
|
getter = getattr(addon, "getSettingBool", None)
|
|
if getter is not None:
|
|
return bool(getter(setting_id))
|
|
raw = addon.getSetting(setting_id)
|
|
return str(raw).strip().lower() in {"1", "true", "yes", "on"}
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
def notify_url(addon_id: str, *, heading: str, url: str, enabled_setting_id: str) -> None:
|
|
if xbmcgui is None:
|
|
return
|
|
if not get_setting_bool(addon_id, enabled_setting_id, default=False):
|
|
return
|
|
try:
|
|
xbmcgui.Dialog().notification(heading, url, xbmcgui.NOTIFICATION_INFO, 3000)
|
|
except Exception:
|
|
return
|
|
|
|
|
|
def _profile_logs_dir(addon_id: str) -> Optional[str]:
|
|
if xbmcaddon is None or xbmcvfs is None:
|
|
return None
|
|
try:
|
|
addon = xbmcaddon.Addon(addon_id)
|
|
profile = xbmcvfs.translatePath(addon.getAddonInfo("profile"))
|
|
log_dir = os.path.join(profile, "logs")
|
|
if not xbmcvfs.exists(log_dir):
|
|
xbmcvfs.mkdirs(log_dir)
|
|
return log_dir
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _append_text_file(path: str, content: str) -> None:
|
|
try:
|
|
with open(path, "a", encoding="utf-8") as handle:
|
|
handle.write(content)
|
|
return
|
|
except Exception:
|
|
pass
|
|
if xbmcvfs is None:
|
|
return
|
|
try:
|
|
handle = xbmcvfs.File(path, "a")
|
|
handle.write(content)
|
|
handle.close()
|
|
except Exception:
|
|
return
|
|
|
|
|
|
def log_url(addon_id: str, *, enabled_setting_id: str, log_filename: str, url: str, kind: str = "VISIT") -> None:
|
|
if not get_setting_bool(addon_id, enabled_setting_id, default=False):
|
|
return
|
|
timestamp = datetime.utcnow().isoformat(timespec="seconds") + "Z"
|
|
line = f"{timestamp}\t{kind}\t{url}\n"
|
|
log_dir = _profile_logs_dir(addon_id)
|
|
if log_dir:
|
|
_append_text_file(os.path.join(log_dir, log_filename), line)
|
|
return
|
|
_append_text_file(os.path.join(os.path.dirname(__file__), log_filename), line)
|
|
|
|
|
|
def dump_response_html(
|
|
addon_id: str,
|
|
*,
|
|
enabled_setting_id: str,
|
|
url: str,
|
|
body: str,
|
|
filename_prefix: str,
|
|
) -> None:
|
|
if not get_setting_bool(addon_id, enabled_setting_id, default=False):
|
|
return
|
|
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S_%f")
|
|
digest = hashlib.md5(url.encode("utf-8")).hexdigest() # nosec - filename only
|
|
filename = f"{filename_prefix}_{timestamp}_{digest}.html"
|
|
log_dir = _profile_logs_dir(addon_id)
|
|
path = os.path.join(log_dir, filename) if log_dir else os.path.join(os.path.dirname(__file__), filename)
|
|
content = f"<!-- {url} -->\n{body or ''}"
|
|
_append_text_file(path, content)
|
|
|