"""Video side shell: the Music ↔ Video toggle + video sidebar (experimental branch). This is the first slice of the video side. It must be ADDITIVE and ISOLATED: the music sidebar/nav is untouched, the toggle + video nav are wired purely via data-attributes (no inline onclick — which would also break the script-split integrity contract), and the controller is a self-contained IIFE that adds no globals. These pin all of that so a regression can't silently couple the two sides or break the music shell. """ from __future__ import annotations import re from pathlib import Path _ROOT = Path(__file__).resolve().parent.parent _INDEX = (_ROOT / "webui" / "index.html").read_text(encoding="utf-8", errors="replace") _JS = (_ROOT / "webui" / "static" / "video" / "video-side.js").read_text(encoding="utf-8") _DASH_JS = (_ROOT / "webui" / "static" / "video" / "video-dashboard.js").read_text(encoding="utf-8") _LIB_JS = (_ROOT / "webui" / "static" / "video" / "video-library.js").read_text(encoding="utf-8") _SCAN_JS = (_ROOT / "webui" / "static" / "video" / "video-scan.js").read_text(encoding="utf-8") _VSETTINGS_JS = (_ROOT / "webui" / "static" / "video" / "video-settings.js").read_text(encoding="utf-8") _CSS_PATH = _ROOT / "webui" / "static" / "video" / "video-side.css" EXPECTED_VIDEO_PAGES = { "video-dashboard", "video-search", "video-discover", "video-library", "video-watchlist", "video-wishlist", "video-downloads", "video-calendar", "video-automations", "video-tools", "video-import", "video-settings", "video-issues", "video-help", } # Reads of sibling VIDEO-side module handles + standard DOM/browser APIs are fine — # what the isolation guard actually forbids is reaching into a MUSIC global (or # leaking a foreign one). So we check every window.* reference is a known-legit one # rather than banning the substring outright (which wrongly flags window.confirm, # window.VideoGet, window.addEventListener, …). _ALLOWED_WINDOW = { # sibling video-side module namespaces (each published by its own IIFE) "window.VideoGet", "window.VideoWatchlist", "window.VideoYoutube", "window.VideoDownload", "window.videoWorkerOrbs", "window._videoWorkerOrbsEnabled", "window._buildAutomationSection", "window._buildAutomationHub", "window._reloadVideoAutomations", "window._vdpgAnyActive", "window._reduceEffectsActive", # standard DOM / browser APIs "window.location", "window.history", "window.matchMedia", "window.addEventListener", "window.removeEventListener", "window.innerWidth", "window.confirm", } def _window_isolated(src: str) -> bool: """True when every ``window.*`` reference is a sibling-video handle or a DOM API (no music global leaked in).""" return set(re.findall(r"window\.\w+", src)) <= _ALLOWED_WINDOW def _block(html: str, open_tag_re: str, close_tag: str) -> str: """Return the substring from the first match of open_tag_re to the next close_tag.""" m = re.search(open_tag_re, html) assert m, f"could not find {open_tag_re!r}" end = html.index(close_tag, m.end()) return html[m.start():end + len(close_tag)] # --- the toggle ------------------------------------------------------------ def test_side_toggle_has_music_and_video(): block = _block(_INDEX, r'