"""providers/hianime.py — hianime.at source (pystardust/ani-cli#1897 flow). Flow: search page -> ajax episode list (numeric anime id) -> ajax server list (sub/dub groups) -> HD-1 hash (base64 embed url) -> embed page (window.__P = base64(json XOR "otaku-embed-v1")) -> master m3u8. The CDN hard-403s without the embed origin as Referer, so _master_m3u8 records it in REFERER and registers the playlist hosts in STREAM_HOSTS; the /stream proxy reads both via stream_meta(). """ import base64 import json import re from urllib.parse import urljoin, urlparse from ..upstream import _cached, _get, megaplay_master BASE = "https://hianime.at" KEY = b"otaku-embed-v1" REFERER = None # embed origin, set when a master playlist is resolved STREAM_HOSTS = set() # playlist/segment hosts the /stream proxy may fetch SUBS = {} # ep_id -> external .vtt url (the player bakes no subs) def _search(q): html = _get(f"{BASE}/search", params={"keyword": q}).text # the top-10 sidebar repeats the result markup; cut it off (ani-cli#1897) html = html.split('id="main-sidebar"')[0] out, seen = [], set() for m in re.finditer( r'