# Copyright (C) 2018 Alexander Seiler
#
# This file is part of plugin.video.srfplaytv.bastelpingu.
#
# Local hotfix 3.0.5 for SRF Play changes (2026-08-15).

import json
import sys
import traceback
import re
from html import unescape as html_unescape
from urllib.parse import parse_qsl, unquote_plus, urlparse, parse_qs, unquote, urljoin

import xbmc
import xbmcaddon
import xbmcgui
import xbmcplugin
import requests

import srgssr


ADDON_ID = "plugin.video.srfplaytv.bastelpingu"
REAL_SETTINGS = xbmcaddon.Addon(id=ADDON_ID)
ADDON_NAME = REAL_SETTINGS.getAddonInfo("name")
ADDON_VERSION = REAL_SETTINGS.getAddonInfo("version")
DEBUG = REAL_SETTINGS.getSetting("Enable_Debugging") == "true"
CONTENT_TYPE = "videos"
INTEGRATION_BASE = "https://il.srgssr.ch/integrationlayer/2.0/video/srf/"
HOTFIX_TAG = "[SRF-BASTELPINGU %s] " % ADDON_VERSION


class SRFPlayTV(srgssr.SRGSSR):
    def __init__(self):
        super(SRFPlayTV, self).__init__(int(sys.argv[1]), bu="srf", addon_id=ADDON_ID)


def log(msg, level=xbmc.LOGDEBUG, always=False):
    """Kodi logger; important hotfix diagnostics are visible even without debug."""
    if not (DEBUG or always or level in (xbmc.LOGINFO, xbmc.LOGWARNING, xbmc.LOGERROR)):
        return
    if level == xbmc.LOGERROR and DEBUG:
        tb = traceback.format_exc()
        if tb and tb != "NoneType: None\n":
            msg += " | " + tb.replace("\n", " | ")
    xbmc.log(HOTFIX_TAG + str(msg), level)


def diag(msg):
    log(msg, xbmc.LOGINFO, always=True)


def get_params():
    return dict(parse_qsl(sys.argv[2][1:]))


def _unwrap_direct_list(payload):
    """Return a list from current/common Play-v3 response shapes."""
    if isinstance(payload, list):
        return payload
    if not isinstance(payload, dict):
        return []

    # /shows currently uses {"data": [...]}; videos-by-show-id commonly uses
    # {"data": {"data": [...]}}. Keep a few compatible fallbacks.
    data = payload.get("data", [])
    if isinstance(data, list):
        return data
    if isinstance(data, dict):
        for key in ("data", "shows", "results", "medias", "videos", "topics"):
            value = data.get(key)
            if isinstance(value, list):
                return value
    for key in ("shows", "results", "medias", "videos", "topics"):
        value = payload.get(key)
        if isinstance(value, list):
            return value
    return []


def _unwrap_single_object(payload):
    if not isinstance(payload, dict):
        return None
    if payload.get("urn") or payload.get("id"):
        return payload
    data = payload.get("data")
    if isinstance(data, dict):
        if data.get("urn") or data.get("id"):
            return data
        for key in ("show", "media", "video"):
            value = data.get(key)
            if isinstance(value, dict):
                return value
    return None


def _normalize_identifier(value):
    if isinstance(value, dict):
        value = value.get("id") or value.get("urn")
    if value is None:
        return None
    value = str(value).strip()
    if not value:
        return None
    return value.rsplit(":", 1)[-1]


def _show_identifier_set(show):
    if not isinstance(show, dict):
        return set()
    values = (show.get("id"), show.get("urn"))
    return {x for x in (_normalize_identifier(v) for v in values) if x}


def _load_json(plugin, url, label):
    try:
        raw = plugin.open_url(url)
        if not raw:
            diag("%s: HTTP/API response empty" % label)
            return None
        payload = json.loads(raw)
        return payload
    except Exception:
        log("%s: JSON/API error" % label, xbmc.LOGERROR, always=True)
        return None


def _load_shows(plugin):
    payload = _load_json(plugin, plugin.apiv3_url + "shows", "shows")
    shows = _unwrap_direct_list(payload)
    shows = [item for item in shows if isinstance(item, dict)]
    diag("shows: %d entries" % len(shows))
    return shows


def _safe_art(data, plugin):
    image = (
        data.get("imageUrl")
        or data.get("image")
        or data.get("posterImageUrl")
        or data.get("thumbnailUrl")
    )
    poster = data.get("posterImageUrl") or image
    return {
        "thumb": image or plugin.icon,
        "poster": poster or image or plugin.icon,
        "fanart": image or plugin.fanart,
        "banner": image or plugin.icon,
    }


def _media_urn(data, kind):
    urn = data.get("urn") if isinstance(data, dict) else None
    if urn:
        return str(urn)
    ident = _normalize_identifier(data.get("id") if isinstance(data, dict) else None)
    if ident:
        return "urn:srf:%s:%s" % (kind, ident)
    return None


def _add_urn_entry(plugin, data, kind="video"):
    """Render using the same navigation contract as upstream: mode=100 + URN."""
    if not isinstance(data, dict):
        return False
    urn = _media_urn(data, kind)
    if not urn:
        return False

    title = (
        data.get("title")
        or data.get("showTitle")
        or data.get("subtitle")
        or urn
    )
    description = data.get("description") or data.get("lead") or ""
    item = xbmcgui.ListItem(label=title)
    item.setInfo(
        "video",
        {
            "title": title,
            "plot": description,
            "plotoutline": data.get("lead") or description,
        },
    )
    item.setArt(_safe_art(data, plugin))
    item.setProperty("IsPlayable", "false")
    xbmcplugin.addDirectoryItem(
        handle=plugin.handle,
        url=plugin.build_url(mode=100, name=urn),
        listitem=item,
        isFolder=True,
    )
    return True


def _add_folder(plugin, label, mode, name):
    item = xbmcgui.ListItem(label=label)
    item.setProperty("IsPlayable", "false")
    item.setArt({"thumb": plugin.icon, "fanart": plugin.fanart})
    xbmcplugin.addDirectoryItem(
        handle=plugin.handle,
        url=plugin.build_url(mode=mode, name=name),
        listitem=item,
        isFolder=True,
    )


def _add_action(plugin, label, mode, name="action"):
    item = xbmcgui.ListItem(label=label)
    item.setProperty("IsPlayable", "false")
    item.setArt({"thumb": plugin.icon, "fanart": plugin.fanart})
    xbmcplugin.addDirectoryItem(
        handle=plugin.handle,
        url=plugin.build_url(mode=mode, name=name),
        listitem=item,
        isFolder=False,
    )


def build_topic_shows_menu(plugin, urn):
    topic_id = _normalize_identifier(urn)
    matching = []
    for show in _load_shows(plugin):
        topic_list = show.get("topicList")
        if not isinstance(topic_list, list):
            continue
        topic_ids = {_normalize_identifier(item) for item in topic_list}
        if topic_id not in topic_ids:
            continue
        episode_count = show.get("numberOfEpisodes")
        if isinstance(episode_count, (int, float)) and episode_count <= 0:
            continue
        matching.append(show)

    matching.sort(key=lambda item: (item.get("title") or "").casefold())
    diag("topic %s: %d shows" % (topic_id, len(matching)))
    for show in matching:
        try:
            # This path is already proven on the user's system; keep upstream renderer.
            plugin.menu_builder.build_entry_apiv3(show, is_show=True)
        except Exception:
            # Manual fallback if a future field breaks upstream rendering.
            _add_urn_entry(plugin, show, kind="show")


def _stored_favourites(plugin):
    stored = plugin.storage_manager.read_favourite_show_ids() or []
    # Preserve order and both bare IDs / legacy URNs.
    norm = []
    for value in stored:
        ident = _normalize_identifier(value)
        if ident and ident not in norm:
            norm.append(ident)
    return stored, norm


def _resolve_favourite_shows(plugin):
    stored, stored_norm = _stored_favourites(plugin)
    if not stored_norm:
        diag("favourites: 0 stored IDs")
        return [], stored, stored_norm

    wanted = set(stored_norm)
    matched = []
    matched_ids = set()
    for show in _load_shows(plugin):
        hits = _show_identifier_set(show).intersection(wanted)
        if hits:
            matched.append(show)
            matched_ids.update(hits)

    # Optional direct endpoint fallback for legacy/removed catalogue entries.
    for sid in stored_norm:
        if sid in matched_ids:
            continue
        payload = _load_json(plugin, plugin.apiv3_url + "shows/" + sid, "show/%s" % sid)
        show = _unwrap_single_object(payload)
        if isinstance(show, dict):
            matched.append(show)

    unique = []
    seen = set()
    for show in matched:
        key = _normalize_identifier(show.get("id") or show.get("urn"))
        if key and key in seen:
            continue
        if key:
            seen.add(key)
        unique.append(show)
    return unique, stored, stored_norm


def build_favourite_shows_menu_hotfix(plugin):
    # Make the otherwise hidden setting reachable directly from the empty menu.
    _add_action(plugin, plugin.plugin_language(30005), 19, "manage-favourites")

    shows, stored, stored_norm = _resolve_favourite_shows(plugin)
    shows.sort(key=lambda item: (item.get("title") or "").casefold())
    diag(
        "favourite shows: stored=%d normalized=%d resolved=%d"
        % (len(stored), len(stored_norm), len(shows))
    )
    for show in shows:
        try:
            plugin.menu_builder.build_entry_apiv3(show, is_show=True)
        except Exception:
            if not _add_urn_entry(plugin, show, kind="show"):
                log("favourite shows: entry could not be rendered", xbmc.LOGWARNING, always=True)


def _videos_for_show(plugin, sid):
    payload = _load_json(
        plugin,
        plugin.apiv3_url + "videos-by-show-id?showId=" + sid,
        "videos-by-show-id/%s" % sid,
    )
    return [x for x in _unwrap_direct_list(payload) if isinstance(x, dict)]


def build_newest_favourite_menu_hotfix(plugin):
    _add_action(plugin, plugin.plugin_language(30005), 19, "manage-favourites")
    _, stored_ids = _stored_favourites(plugin)
    if not stored_ids:
        diag("newest favourites: no stored favourites")
        return

    items = []
    seen = set()
    for sid in stored_ids:
        videos = _videos_for_show(plugin, sid)
        diag("newest favourites: show %s -> %d videos" % (sid, len(videos)))
        for video in videos:
            key = video.get("urn") or video.get("id")
            if key and key in seen:
                continue
            if key:
                seen.add(key)
            items.append(video)

    items.sort(key=lambda item: item.get("date") or item.get("publishedDate") or "", reverse=True)
    diag("newest favourites: total %d videos" % len(items))
    for video in items:
        # Use upstream renderer first; it creates the normal mode=100 URN navigation.
        try:
            plugin.menu_builder.build_entry_apiv3(video)
        except Exception:
            if not _add_urn_entry(plugin, video, kind="video"):
                log("newest favourites: entry could not be rendered", xbmc.LOGWARNING, always=True)


def manage_favourite_shows_hotfix(plugin):
    shows = _load_shows(plugin)
    shows.sort(key=lambda item: (item.get("title") or "").casefold())
    if not shows:
        xbmcgui.Dialog().notification(ADDON_NAME, "Keine Sendungen von SRF geladen", plugin.icon, 4000)
        diag("favourite manager: catalogue empty")
        return

    stored, stored_norm_list = _stored_favourites(plugin)
    stored_norm = set(stored_norm_list)
    names = [show.get("title") or show.get("urn") or show.get("id") or "?" for show in shows]
    preselect = []
    current_ids = set()
    for index, show in enumerate(shows):
        ids = _show_identifier_set(show)
        current_ids.update(ids)
        if ids.intersection(stored_norm):
            preselect.append(index)

    ancient = [value for value in stored if _normalize_identifier(value) not in current_ids]
    selected = xbmcgui.Dialog().multiselect(
        plugin.plugin_language(30069), names, preselect=preselect
    )
    if selected is None:
        diag("favourite manager: cancelled")
        return

    new_ids = []
    for index in selected:
        show = shows[index]
        sid = _normalize_identifier(show.get("id") or show.get("urn"))
        if sid and sid not in new_ids:
            new_ids.append(sid)
    new_ids += ancient
    plugin.storage_manager.write_favourite_show_ids(new_ids)
    diag("favourite manager: wrote %d favourites" % len(new_ids))
    xbmcgui.Dialog().notification(
        ADDON_NAME,
        "%d bevorzugte Sendungen gespeichert" % len(new_ids),
        plugin.icon,
        3000,
    )


def build_homepage_hotfix(plugin):
    """Static, guaranteed-nonempty replacement for broken Remix homepage scraper."""
    _add_folder(plugin, plugin.plugin_language(30110), 201, "latest")
    _add_folder(plugin, plugin.plugin_language(30111), 202, "trending")
    _add_folder(plugin, plugin.plugin_language(30112), 13, "Topics")
    _add_folder(plugin, plugin.plugin_language(30057), 17, "Shows_By_Date")
    _add_folder(plugin, plugin.plugin_language(30050), 10, "All_Shows")
    diag("homepage: added 5 local folders")


def _extract_chapters(payload):
    if not isinstance(payload, dict):
        return []
    for node in (payload, payload.get("data") if isinstance(payload.get("data"), dict) else None):
        if not isinstance(node, dict):
            continue
        for key in ("chapterList", "chapters", "mediaList", "medias", "results"):
            value = node.get(key)
            if isinstance(value, list):
                return value
    return []


def _silent_http_text(url, label):
    """HTTP without srgssr.open_url() GUI notifications; used for optional discovery feeds."""
    try:
        response = requests.get(
            url,
            headers={
                "User-Agent": "Mozilla/5.0 (Kodi; SRF Play TV %s)" % ADDON_VERSION,
                "Accept": "application/json,text/html,application/xhtml+xml;q=0.9,*/*;q=0.8",
            },
            timeout=12,
        )
        diag("%s: HTTP %s, %d bytes" % (label, response.status_code, len(response.content or b"")))
        # SRF currently serves the Play homepage as UTF-8. requests can fall back
        # to ISO-8859-1 when the response header has no explicit charset, which
        # turns umlauts into mojibake (e.g. "fÃ¼r"). Force UTF-8 here.
        response.encoding = "UTF-8"
        if not response.ok or not response.text:
            return None
        return response.text
    except Exception as exc:
        diag("%s: silent HTTP failed: %s" % (label, exc))
        return None


def _load_integration_payload(feed):
    # Current SRG docs show the endpoint without an extension, while older clients
    # used .json. Try both silently because the deployment has varied over time.
    urls = [
        INTEGRATION_BASE + feed,
        INTEGRATION_BASE + feed + ".json",
    ]
    for idx, url in enumerate(urls, 1):
        raw = _silent_http_text(url, "integration/%s try%d" % (feed, idx))
        if not raw:
            continue
        try:
            payload = json.loads(raw)
        except Exception:
            diag("integration/%s try%d: response not JSON" % (feed, idx))
            continue
        if _extract_chapters(payload):
            return payload
        # Return only if it is non-empty and appears to contain a known list shape.
        if isinstance(payload, dict) and any(k in payload for k in ("chapterList", "chapters", "mediaList", "medias", "results")):
            return payload
    return None


def _clean_anchor_text(fragment):
    text = re.sub(r"<script\b.*?</script>|<style\b.*?</style>", " ", fragment, flags=re.I | re.S)
    text = re.sub(r"<[^>]+>", " ", text)
    text = html_unescape(text)
    text = re.sub(r"\s+", " ", text).strip()
    # Remove a few common UI words if they are the only prefix around a card.
    text = re.sub(r"^(Video abspielen|Mehr Infos)\s*", "", text, flags=re.I).strip()
    return text


def _largest_srcset_url(value):
    """Pick the largest candidate URL from an HTML srcset string."""
    if not value:
        return None
    candidates = []
    for part in html_unescape(value).split(","):
        bit = part.strip()
        if not bit:
            continue
        pieces = bit.rsplit(None, 1)
        url = pieces[0].strip()
        score = 0
        if len(pieces) > 1:
            descriptor = pieces[1].lower()
            try:
                if descriptor.endswith("w"):
                    score = int(float(descriptor[:-1]))
                elif descriptor.endswith("x"):
                    score = int(float(descriptor[:-1]) * 1000)
            except Exception:
                score = 0
        candidates.append((score, url))
    if not candidates:
        return None
    candidates.sort(key=lambda item: item[0], reverse=True)
    return candidates[0][1]


def _extract_image_from_fragment(fragment):
    """Extract the best SRF card image from <picture>/<img> markup."""
    if not fragment:
        return None

    # Prefer srcset because SRF often puts the useful high-resolution image on
    # <source> while <img src> may contain a tiny/lazy placeholder.
    srcsets = re.findall(
        r'''(?:srcset|data-srcset)\s*=\s*["']([^"']+)["']''',
        fragment,
        flags=re.I,
    )
    best = None
    best_score = -1
    for value in srcsets:
        candidate = _largest_srcset_url(value)
        if not candidate:
            continue
        score = 2 if candidate.startswith("https://") else 1
        if score > best_score and not candidate.startswith("data:"):
            best = candidate
            best_score = score
    if best:
        return html_unescape(best)

    for attr in ("data-src", "data-original", "src"):
        match = re.search(
            r'''\b%s\s*=\s*["']([^"']+)["']''' % re.escape(attr),
            fragment,
            flags=re.I,
        )
        if match:
            candidate = html_unescape(match.group(1).strip())
            if candidate and not candidate.startswith(("data:", "blob:")):
                return candidate
    return None


def _extract_meta_image(html):
    """Extract og:image/twitter:image/thumbnailUrl from a SRF video page."""
    if not html:
        return None
    patterns = (
        r'''<meta\b[^>]*(?:property|name)=["'](?:og:image|twitter:image)["'][^>]*content=["']([^"']+)["']''',
        r'''<meta\b[^>]*content=["']([^"']+)["'][^>]*(?:property|name)=["'](?:og:image|twitter:image)["']''',
        r'''["']thumbnailUrl["']\s*:\s*["']([^"']+)["']''',
        r'''["']imageUrl["']\s*:\s*["'](https?://[^"']+)["']''',
    )
    for pattern in patterns:
        match = re.search(pattern, html, flags=re.I | re.S)
        if match:
            value = html_unescape(match.group(1)).replace(r"\u0026", "&").replace(r"\/", "/")
            if value.startswith("http://") or value.startswith("https://"):
                return value
    return None


def _enrich_card_art(cards, feed):
    """Fill missing card artwork from the canonical SRF video pages."""
    enriched = 0
    for card in cards:
        if card.get("imageUrl"):
            continue
        page_url = card.get("pageUrl")
        if not page_url:
            continue
        raw = _silent_http_text(page_url, "homepage art/%s" % feed)
        image = _extract_meta_image(raw)
        if image:
            card["imageUrl"] = image
            card["posterImageUrl"] = image
            enriched += 1
    diag("homepage art/%s: enriched %d/%d cards" % (feed, enriched, len(cards)))
    return cards


def _homepage_video_cards(html, feed):
    """Extract current Play-SRF SSR cards from the public homepage as a fallback."""
    if not html:
        return []

    # For latest, limit to the actual "Neu & empfehlenswert" shelf when possible.
    # For trending, use the highlighted cards before that shelf (hero/top carousel).
    lower = html.lower()
    markers = ["neu &amp; empfehlenswert", "neu & empfehlenswert", "neu &amp;amp; empfehlenswert"]
    latest_pos = -1
    for marker in markers:
        pos = lower.find(marker)
        if pos >= 0:
            latest_pos = pos
            break

    if feed == "latest" and latest_pos >= 0:
        section = html[latest_pos:]
        sec_lower = section.lower()
        stops = [sec_lower.find(x) for x in ("kategorien", "categories") if sec_lower.find(x) > 0]
        if stops:
            section = section[:min(stops)]
    elif feed == "trending" and latest_pos > 0:
        # The top/hero cards are the most prominently featured items on Play SRF.
        # This is a public-homepage fallback when the dedicated IL trending feed is unavailable.
        section = html[:latest_pos]
    else:
        section = html

    # Links currently carry a canonical ?urn=urn:srf:video:... query value.
    anchor_re = re.compile(
        r'<a\b[^>]*href=["\']([^"\']*(?:\?|&amp;|&)urn=[^"\']+)["\'][^>]*>(.*?)</a>',
        re.I | re.S,
    )
    cards = []
    seen = set()
    for href, body in anchor_re.findall(section):
        href_decoded = html_unescape(href)
        try:
            query = parse_qs(urlparse(href_decoded).query)
            urn = (query.get("urn") or [None])[0]
        except Exception:
            urn = None
        if not urn:
            # Some SSR variants keep it encoded in the href string.
            m = re.search(r'(urn(?::|%3A)srf(?::|%3A)video(?::|%3A)[0-9a-f-]{20,})', href_decoded, re.I)
            urn = unquote(m.group(1)) if m else None
        if not urn or not str(urn).startswith("urn:srf:video:") or urn in seen:
            continue
        seen.add(urn)
        title = _clean_anchor_text(body)
        if not title:
            # Derive a readable fallback from the last URL path segment.
            slug = urlparse(href_decoded).path.rstrip("/").split("/")[-1]
            title = unquote(slug).replace("-", " ").strip().title() or "SRF Video"
        image = _extract_image_from_fragment(body)
        if image:
            image = urljoin("https://www.srf.ch", image)
        cards.append(
            {
                "urn": urn,
                "title": title,
                "imageUrl": image,
                "posterImageUrl": image,
                "pageUrl": urljoin("https://www.srf.ch", href_decoded),
            }
        )
        if len(cards) >= 30:
            break
    diag(
        "homepage scrape/%s: %d video cards, %d with inline art"
        % (feed, len(cards), sum(1 for card in cards if card.get("imageUrl")))
    )
    return cards


def _render_cards(plugin, cards, feed):
    added = 0
    for card in cards:
        urn = card.get("urn")
        title = card.get("title") or urn
        if not urn:
            continue
        item = xbmcgui.ListItem(label=title)
        item.setInfo("video", {"title": title})
        image = card.get("imageUrl") or card.get("posterImageUrl")
        item.setArt(
            {
                "thumb": image or plugin.icon,
                "poster": card.get("posterImageUrl") or image or plugin.icon,
                "fanart": image or plugin.fanart,
                "banner": image or plugin.icon,
            }
        )
        item.setProperty("IsPlayable", "false")
        xbmcplugin.addDirectoryItem(
            handle=plugin.handle,
            url=plugin.build_url(mode=100, name=urn),
            listitem=item,
            isFolder=True,
        )
        added += 1
    diag("homepage scrape/%s: rendered %d items" % (feed, added))
    return added


def build_integration_feed(plugin, feed):
    """Latest/trending with silent IL lookup and current Play-SRF homepage fallback."""
    if feed not in ("latest", "trending"):
        return

    payload = _load_integration_payload(feed)
    chapters = _extract_chapters(payload)
    diag("integration/%s: %d items" % (feed, len(chapters)))

    added = 0
    for chapter in chapters:
        try:
            if _add_urn_entry(plugin, chapter, kind="video"):
                added += 1
        except Exception:
            log("integration/%s: entry failed" % feed, xbmc.LOGERROR, always=True)
    diag("integration/%s: rendered %d items" % (feed, added))

    if added == 0:
        homepage = _silent_http_text("https://www.srf.ch/play/tv", "homepage scrape/%s" % feed)
        cards = _homepage_video_cards(homepage, feed)
        if cards and any(not card.get("imageUrl") for card in cards):
            cards = _enrich_card_art(cards, feed)
        added = _render_cards(plugin, cards, feed)

    # Never strand the user in a blank page and never show a URL-error popup.
    if added == 0:
        _add_folder(plugin, plugin.plugin_language(30057), 17, "Shows_By_Date")
        _add_folder(plugin, plugin.plugin_language(30050), 10, "All_Shows")
        _add_folder(plugin, plugin.plugin_language(30112), 13, "Topics")
        diag("integration/%s: fallback folders added" % feed)

def run():
    params = get_params()
    try:
        url = unquote_plus(params["url"])
    except Exception:
        url = None
    try:
        name = unquote_plus(params["name"])
    except Exception:
        name = None
    try:
        mode = int(params["mode"])
    except Exception:
        mode = None
    try:
        page_hash = unquote_plus(params["page_hash"])
    except Exception:
        page_hash = None
    try:
        page = unquote_plus(params["page"])
    except Exception:
        page = None

    plugin = SRFPlayTV()
    diag("run mode=%s name=%s" % (mode, name))

    if mode is None:
        identifiers = [
            "All_Shows",
            "Favourite_Shows",
            "Newest_Favourite_Shows",
            "Homepage",
            "Topics",
            "Shows_By_Date",
            "Search",
            "SRF_YouTube",
        ]
        plugin.menu_builder.build_main_menu(identifiers)
    elif mode == 10:
        plugin.menu_builder.build_all_shows_menu()
    elif mode == 11:
        build_favourite_shows_menu_hotfix(plugin)
    elif mode == 12:
        build_newest_favourite_menu_hotfix(plugin)
    elif mode == 13:
        plugin.menu_builder.build_topics_menu()
    elif mode == 17:
        plugin.menu_builder.build_dates_overview_menu()
    elif mode == 19:
        manage_favourite_shows_hotfix(plugin)
    elif mode == 21:
        plugin.menu_builder.build_episode_menu(name)
    elif mode == 24:
        plugin.menu_builder.build_date_menu(name)
    elif mode == 60:
        plugin.menu_builder.build_specific_date_menu(name)
    elif mode == 25:
        plugin.menu_builder.pick_date()
    elif mode == 27:
        plugin.menu_builder.build_search_menu()
    elif mode == 28:
        plugin.menu_builder.build_search_media_menu(
            mode=mode, name=name, page=page, page_hash=page_hash
        )
    elif mode == 70:
        plugin.menu_builder.build_recent_search_menu()
    elif mode == 30:
        plugin.youtube_builder.build_youtube_channel_overview_menu(33)
    elif mode == 33:
        plugin.youtube_builder.build_youtube_channel_menu(
            name, mode, page=page, page_token=page_hash
        )
    elif mode == 50:
        plugin.player.play_video(name)
    elif mode == 100:
        if name and ":topic:" in name:
            build_topic_shows_menu(plugin, name)
        else:
            plugin.menu_builder.build_menu_by_urn(name)
    elif mode == 200:
        build_homepage_hotfix(plugin)
    elif mode == 201:
        build_integration_feed(plugin, "latest")
    elif mode == 202:
        build_integration_feed(plugin, "trending")
    elif mode == 1000:
        plugin.menu_builder.build_menu_apiv3(name, mode, page, page_hash)

    xbmcplugin.setContent(int(sys.argv[1]), CONTENT_TYPE)
    xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
    xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_NONE)
    xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_LABEL)
    xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_TITLE)
    xbmcplugin.endOfDirectory(int(sys.argv[1]), cacheToDisc=True)
