dev: YouTube Fixes, Trakt Credentials fest, Upcoming Ansicht, Watchlist Kontextmenue
This commit is contained in:
@@ -18,7 +18,14 @@ except ImportError:
|
||||
requests = None # type: ignore
|
||||
|
||||
from plugin_interface import BasisPlugin
|
||||
from plugin_helpers import log_error
|
||||
|
||||
try:
|
||||
import xbmc # type: ignore
|
||||
def _log(msg: str) -> None:
|
||||
xbmc.log(f"[ViewIt][YouTube] {msg}", xbmc.LOGWARNING)
|
||||
except ImportError:
|
||||
def _log(msg: str) -> None:
|
||||
pass
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Konstanten
|
||||
@@ -121,13 +128,15 @@ def _videos_from_search_data(data: dict) -> List[str]:
|
||||
if title and video_id:
|
||||
results.append(_encode(title, video_id))
|
||||
except Exception as exc:
|
||||
log_error(f"[YouTube] _videos_from_search_data Fehler: {exc}")
|
||||
_log(f"[YouTube] _videos_from_search_data Fehler: {exc}")
|
||||
return results
|
||||
|
||||
|
||||
|
||||
def _search_with_ytdlp(query: str, count: int = 20) -> List[str]:
|
||||
"""Sucht YouTube-Videos via yt-dlp ytsearch-Extraktor."""
|
||||
if not _ensure_ytdlp_in_path():
|
||||
return []
|
||||
try:
|
||||
from yt_dlp import YoutubeDL # type: ignore
|
||||
except ImportError:
|
||||
@@ -144,7 +153,7 @@ def _search_with_ytdlp(query: str, count: int = 20) -> List[str]:
|
||||
if e.get("id") and e.get("title")
|
||||
]
|
||||
except Exception as exc:
|
||||
log_error(f"[YouTube] yt-dlp Suche Fehler: {exc}")
|
||||
_log(f"[YouTube] yt-dlp Suche Fehler: {exc}")
|
||||
return []
|
||||
|
||||
|
||||
@@ -161,16 +170,60 @@ def _fetch_search_videos(url: str) -> List[str]:
|
||||
return []
|
||||
return _videos_from_search_data(data)
|
||||
except Exception as exc:
|
||||
log_error(f"[YouTube] _fetch_search_videos ({url}): {exc}")
|
||||
_log(f"[YouTube] _fetch_search_videos ({url}): {exc}")
|
||||
return []
|
||||
|
||||
|
||||
def _fix_strptime() -> None:
|
||||
"""Kodi-Workaround: datetime.strptime ist manchmal None."""
|
||||
import datetime as _dt
|
||||
import time as _time
|
||||
if not callable(getattr(_dt.datetime, "strptime", None)):
|
||||
_dt.datetime.strptime = lambda s, f: _dt.datetime(*(_time.strptime(s, f)[0:6]))
|
||||
|
||||
|
||||
def _ensure_ytdlp_in_path() -> bool:
|
||||
"""Fuegt script.module.yt-dlp/lib zum sys.path hinzu falls noetig."""
|
||||
_fix_strptime()
|
||||
try:
|
||||
import yt_dlp # type: ignore # noqa: F401
|
||||
return True
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
import sys, os
|
||||
import xbmcvfs # type: ignore
|
||||
lib_path = xbmcvfs.translatePath("special://home/addons/script.module.yt-dlp/lib")
|
||||
if lib_path and os.path.isdir(lib_path) and lib_path not in sys.path:
|
||||
sys.path.insert(0, lib_path)
|
||||
import yt_dlp # type: ignore # noqa: F401
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _get_quality_format() -> str:
|
||||
"""Liest YouTube-Qualitaet aus den Addon-Einstellungen."""
|
||||
_QUALITY_MAP = {
|
||||
"0": "best[ext=mp4]/best",
|
||||
"1": "bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080][ext=mp4]/best",
|
||||
"2": "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720][ext=mp4]/best",
|
||||
"3": "bestvideo[height<=480][ext=mp4]+bestaudio[ext=m4a]/best[height<=480][ext=mp4]/best",
|
||||
"4": "bestvideo[height<=360][ext=mp4]+bestaudio[ext=m4a]/best[height<=360][ext=mp4]/best",
|
||||
}
|
||||
try:
|
||||
import xbmcaddon # type: ignore
|
||||
val = xbmcaddon.Addon().getSetting("youtube_quality") or "0"
|
||||
return _QUALITY_MAP.get(val, _QUALITY_MAP["0"])
|
||||
except Exception:
|
||||
return _QUALITY_MAP["0"]
|
||||
|
||||
|
||||
def _resolve_with_ytdlp(video_id: str) -> Optional[str]:
|
||||
"""Loest Video-ID via yt-dlp zu direkter Stream-URL auf."""
|
||||
try:
|
||||
from yt_dlp import YoutubeDL # type: ignore
|
||||
except ImportError:
|
||||
log_error("[YouTube] yt-dlp nicht verfuegbar (script.module.yt-dlp fehlt)")
|
||||
if not _ensure_ytdlp_in_path():
|
||||
_log("[YouTube] yt-dlp nicht verfuegbar (script.module.yt-dlp fehlt)")
|
||||
try:
|
||||
import xbmcgui
|
||||
xbmcgui.Dialog().notification(
|
||||
@@ -182,9 +235,14 @@ def _resolve_with_ytdlp(video_id: str) -> Optional[str]:
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
try:
|
||||
from yt_dlp import YoutubeDL # type: ignore
|
||||
except ImportError:
|
||||
return None
|
||||
url = f"https://www.youtube.com/watch?v={video_id}"
|
||||
fmt = _get_quality_format()
|
||||
ydl_opts: Dict[str, Any] = {
|
||||
"format": "best[ext=mp4]/best",
|
||||
"format": fmt,
|
||||
"quiet": True,
|
||||
"no_warnings": True,
|
||||
"extract_flat": False,
|
||||
@@ -203,7 +261,7 @@ def _resolve_with_ytdlp(video_id: str) -> Optional[str]:
|
||||
if formats:
|
||||
return formats[-1].get("url")
|
||||
except Exception as exc:
|
||||
log_error(f"[YouTube] yt-dlp Fehler fuer {video_id}: {exc}")
|
||||
_log(f"[YouTube] yt-dlp Fehler fuer {video_id}: {exc}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -214,8 +272,7 @@ def _resolve_with_ytdlp(video_id: str) -> Optional[str]:
|
||||
class YoutubePlugin(BasisPlugin):
|
||||
name = "YouTube"
|
||||
|
||||
# Pseudo-Staffeln: nur Suche – Browse-Endpunkte erfordern Login
|
||||
_SEASONS = ["Suche"]
|
||||
_SEASONS = ["Stream"]
|
||||
|
||||
def capabilities(self) -> Set[str]:
|
||||
return set()
|
||||
@@ -241,8 +298,7 @@ class YoutubePlugin(BasisPlugin):
|
||||
return list(self._SEASONS)
|
||||
|
||||
def episodes_for(self, title: str, season: str) -> List[str]:
|
||||
if season == "Suche":
|
||||
# Titel ist bereits ein kodierter Eintrag aus der Suche
|
||||
if season == "Stream":
|
||||
return [title]
|
||||
return []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user