"""
Jacktook addon router — lazy-loading dispatcher.
Uses prefix-based routing with lazy imports to avoid loading all addon
modules on every invocation. Only the modules needed for the current
action are imported.
"""
import sys
from urllib import parse
# ---------------------------------------------------------------------------
# Action sets for set-based lookups
# ---------------------------------------------------------------------------
_TRAKT_EXTRAS = frozenset(
{
"add_to_collection",
"remove_from_collection",
"list_trakt_page",
}
)
_DEBRID_ACTIONS = frozenset(
{
"rd_auth",
"rd_remove_auth",
"ad_auth",
"ad_remove_auth",
"real_debrid_info",
"alldebrid_info",
"debrider_auth",
"debrider_remove_auth",
"debrider_info",
"get_rd_downloads",
"get_tb_downloads",
"get_oc_downloads",
"cloud",
"cloud_details",
"pm_auth",
"pm_remove_auth",
"tb_auth",
"tb_remove_auth",
"oc_auth",
"oc_remove_auth",
"torbox_info",
"offcloud_info",
"easynews_info",
}
)
_TORRSERVER_ACTIONS = frozenset(
{
"torrents",
"torrent_action",
"torrent_files",
"torrentio_selection",
"display_picture",
"display_text",
"download_torrent_subtitles",
"download_and_play_subtitles",
}
)
_WEBDAV_ACTIONS = frozenset(
{
"list_webdav",
"webdav_provider_test",
"display_text_webdav",
"show_picture_webdav",
}
)
_DOWNLOAD_ACTIONS = frozenset(
{
"download_video",
"handle_download_file",
"handle_cancel_download",
"handle_pause_download",
"handle_delete_file",
"downloads_menu",
"resume_download",
"download_cloud_file",
"open_download_manager",
"downloads_viewer",
}
)
_GUI_ACTIONS = frozenset(
{
"run_next_dialog",
"run_resume_dialog",
"run_skip_intro_dialog",
"test_source_select",
"test_run_next",
"test_resume_dialog",
"test_download_dialog",
"extras",
}
)
_MDBLIST_ACTIONS = frozenset(
{
"search_mdbd_lists",
"user_mdbd_lists",
"top_mdbd_lists",
"show_mdblist_list",
}
)
def _is_stremio_action(action):
return action.startswith(("stremio_", "list_stremio")) or action in {
"clear_stremio_search_history",
"list_catalog",
"list_catalog_genres",
"search_catalog",
"add_custom_stremio_addon",
"remove_custom_stremio_addon",
"rename_stremio_addon",
"rename_stremio_catalog",
"torrentio_toggle_providers",
"stremio_bypass_addons_select",
}
def _is_tmdb_action(action):
return action in (
"tmdb_search_modes",
"tmdb_episode_search_modes",
) or action.startswith(
(
"handle_tmdb",
"search_tmdb",
"search_tmbd",
"rescrape_tmdb",
"show_tmdb",
"handle_collection",
"search_people",
"handle_keyword",
"show_keyword",
)
)
def _is_trakt_action(action):
return action.startswith("trakt_") or action in _TRAKT_EXTRAS
def _is_debrid_action(action):
return action in _DEBRID_ACTIONS
def _is_telegram_action(action):
return action.startswith(("telegram_", "list_telegram", "list_jackgram"))
def _is_torrserver_action(action):
return action in _TORRSERVER_ACTIONS
def _is_webdav_action(action):
return action in _WEBDAV_ACTIONS
def _is_download_action(action):
return action in _DOWNLOAD_ACTIONS
def _is_mdblist_action(action):
return action in _MDBLIST_ACTIONS
def _is_cache_action(action):
return action.startswith("clear_")
def _is_gui_action(action):
return action in _GUI_ACTIONS
def _is_source_manager_action(action):
return action == "source_manager_toggle"
def _get_route_handler(action):
for matcher, handler in ROUTE_GROUPS:
if matcher(action):
return handler
return _route_core
# ---------------------------------------------------------------------------
# Main router
# ---------------------------------------------------------------------------
def addon_router():
param_string = sys.argv[2][1:]
if param_string:
params = dict(parse.parse_qsl(param_string))
action = params.get("action", "")
from lib.utils.tmdb_init import ensure_tmdb_init
ensure_tmdb_init()
route_handler = _get_route_handler(action)
route_handler(action, params)
return
from lib.navigation import root_menu
root_menu()
# ---------------------------------------------------------------------------
# Route handlers — each lazily imports only the modules it needs
# ---------------------------------------------------------------------------
def _route_stremio(action, params):
if action in (
"stremio_toggle_addons",
"stremio_toggle_catalogs",
"stremio_toggle_tv_addons",
"stremio_filtered_selection",
"add_custom_stremio_addon",
"remove_custom_stremio_addon",
"rename_stremio_addon",
"rename_stremio_catalog",
"stremio_bypass_addons_select",
):
from lib.clients.stremio.addon_selection import (
add_custom_stremio_addon,
remove_custom_stremio_addon,
rename_stremio_addon,
rename_stremio_catalog,
stremio_bypass_addons_select,
stremio_filtered_selection,
stremio_toggle_addons,
stremio_toggle_catalogs,
stremio_toggle_tv_addons,
)
actions = {
"stremio_toggle_addons": stremio_toggle_addons,
"stremio_toggle_catalogs": stremio_toggle_catalogs,
"stremio_toggle_tv_addons": stremio_toggle_tv_addons,
"stremio_filtered_selection": stremio_filtered_selection,
"add_custom_stremio_addon": add_custom_stremio_addon,
"remove_custom_stremio_addon": remove_custom_stremio_addon,
"rename_stremio_addon": rename_stremio_addon,
"rename_stremio_catalog": rename_stremio_catalog,
"stremio_bypass_addons_select": stremio_bypass_addons_select,
}
actions[action](params)
elif action == "stremio_manage_phone":
from lib.clients.stremio.manage_phone import stremio_manage_phone
stremio_manage_phone(params)
elif action in ("stremio_login", "stremio_logout", "stremio_update"):
from lib.clients.stremio.authentication import (
stremio_login,
stremio_logout,
stremio_update,
)
actions = {
"stremio_login": stremio_login,
"stremio_logout": stremio_logout,
"stremio_update": stremio_update,
}
actions[action](params)
elif action == "torrentio_toggle_providers":
from lib.clients.stremio.torrentio import torrentio_toggle_providers
torrentio_toggle_providers(params)
else:
from lib.clients.stremio.catalog_menus import (
clear_stremio_search_history,
list_catalog,
list_catalog_genres,
list_stremio_episodes,
list_stremio_movie,
list_stremio_seasons,
list_stremio_tv,
list_stremio_tv_streams,
search_catalog,
)
actions = {
"clear_stremio_search_history": clear_stremio_search_history,
"list_catalog": list_catalog,
"list_catalog_genres": list_catalog_genres,
"list_stremio_seasons": list_stremio_seasons,
"list_stremio_episodes": list_stremio_episodes,
"list_stremio_movie": list_stremio_movie,
"list_stremio_tv_streams": list_stremio_tv_streams,
"list_stremio_tv": list_stremio_tv,
"search_catalog": search_catalog,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_tmdb(action, params):
if action in ("search_tmdb_year", "search_tmdb_genres"):
from lib.clients.tmdb.tmdb import TmdbClient
from lib.utils.general.utils import set_content_type
if action == "search_tmdb_year":
mode = params["mode"]
set_content_type(mode)
TmdbClient.tmdb_search_year(
mode, params["submode"], int(params["year"]), int(params["page"])
)
else:
mode = params["mode"]
set_content_type(mode)
TmdbClient.tmdb_search_genres(
mode,
params["genre_id"],
int(params["page"]),
submode=params.get("submode"),
)
elif action in (
"search_people_by_id",
"handle_tmdb_person_details",
"handle_tmdb_person_info",
):
from lib.clients.tmdb.people_client import PeopleClient
actions = {
"search_people_by_id": PeopleClient.search_people_by_id,
"handle_tmdb_person_details": PeopleClient.handle_tmdb_person_details,
"handle_tmdb_person_info": PeopleClient.handle_tmdb_person_info,
}
actions[action](params)
elif action == "handle_collection_details":
from lib.clients.tmdb.collections import TmdbCollections
TmdbCollections.add_collection_details(params)
else:
from lib.clients.tmdb.tmdb import TmdbClient
actions = {
"handle_tmdb_search": TmdbClient.handle_tmdb_search,
"handle_tmdb_query": TmdbClient.handle_tmdb_query,
"search_tmdb_lang": TmdbClient.show_lang_items,
"search_tmbd_network": TmdbClient.show_network_items,
"search_tmdb_recommendations": TmdbClient.search_tmdb_recommendations,
"search_tmdb_similar": TmdbClient.search_tmdb_similar,
"tmdb_search_modes": TmdbClient.tmdb_search_modes,
"tmdb_episode_search_modes": TmdbClient.tmdb_episode_search_modes,
"rescrape_tmdb_media": TmdbClient.rescrape_tmdb_media,
"show_tmdb_item": TmdbClient.show_tmdb_item,
"handle_collection_query": TmdbClient.handle_collection_query,
"handle_keyword_search": TmdbClient.handle_keyword_search,
"show_keyword_results": TmdbClient.show_keyword_results,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_trakt(action, params):
if action in (
"trakt_add_to_watchlist",
"trakt_remove_from_watchlist",
"trakt_add_to_favorites",
"trakt_remove_from_favorites",
"trakt_mark_as_watched",
"trakt_mark_as_unwatched",
"add_to_collection",
"remove_from_collection",
"trakt_create_list",
"trakt_delete_list",
"trakt_like_list",
"trakt_unlike_list",
"trakt_add_item_to_list",
"trakt_remove_item_from_list",
):
from lib.clients.trakt.trakt import TraktClient
actions = {
"trakt_add_to_watchlist": TraktClient.trakt_add_to_watchlist,
"trakt_remove_from_watchlist": TraktClient.trakt_remove_from_watchlist,
"trakt_add_to_favorites": TraktClient.trakt_add_to_favorites,
"trakt_remove_from_favorites": TraktClient.trakt_remove_from_favorites,
"trakt_mark_as_watched": TraktClient.trakt_mark_as_watched,
"trakt_mark_as_unwatched": TraktClient.trakt_mark_as_unwatched,
"add_to_collection": TraktClient.trakt_add_to_collection,
"remove_from_collection": TraktClient.trakt_remove_from_collection,
"trakt_create_list": TraktClient.trakt_create_list,
"trakt_delete_list": TraktClient.trakt_delete_list,
"trakt_like_list": TraktClient.trakt_like_list,
"trakt_unlike_list": TraktClient.trakt_unlike_list,
"trakt_add_item_to_list": TraktClient.trakt_add_item_to_list,
"trakt_remove_item_from_list": TraktClient.trakt_remove_item_from_list,
}
actions[action](params)
else:
from lib.navigation import (
list_trakt_page,
trakt_auth,
trakt_auth_revoke,
trakt_group_menu,
trakt_list_content,
)
actions = {
"trakt_auth": trakt_auth,
"trakt_auth_revoke": trakt_auth_revoke,
"trakt_group_menu": trakt_group_menu,
"trakt_list_content": trakt_list_content,
"list_trakt_page": list_trakt_page,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_debrid(action, params):
from lib.navigation import (
ad_auth,
ad_remove_auth,
alldebrid_info,
cloud,
cloud_details,
debrider_auth,
debrider_info,
debrider_remove_auth,
easynews_info,
get_oc_downloads,
get_rd_downloads,
get_tb_downloads,
offcloud_info,
oc_auth,
oc_remove_auth,
pm_auth,
pm_remove_auth,
rd_auth,
rd_remove_auth,
real_debrid_info,
tb_auth,
tb_remove_auth,
torbox_info,
)
actions = {
"rd_auth": rd_auth,
"rd_remove_auth": rd_remove_auth,
"ad_auth": ad_auth,
"ad_remove_auth": ad_remove_auth,
"real_debrid_info": real_debrid_info,
"alldebrid_info": alldebrid_info,
"debrider_auth": debrider_auth,
"debrider_remove_auth": debrider_remove_auth,
"debrider_info": debrider_info,
"get_rd_downloads": get_rd_downloads,
"get_tb_downloads": get_tb_downloads,
"get_oc_downloads": get_oc_downloads,
"cloud": cloud,
"cloud_details": cloud_details,
"pm_auth": pm_auth,
"pm_remove_auth": pm_remove_auth,
"tb_auth": tb_auth,
"tb_remove_auth": tb_remove_auth,
"oc_auth": oc_auth,
"oc_remove_auth": oc_remove_auth,
"torbox_info": torbox_info,
"offcloud_info": offcloud_info,
"easynews_info": easynews_info,
}
actions[action](params)
def _route_telegram(action, params):
if action == "telegram_menu":
from lib.navigation import telegram_menu
telegram_menu(params)
else:
from lib.clients.jackgram.utils import (
list_jackgram_latest_movies,
list_jackgram_latest_series,
list_jackgram_raw_files,
list_jackgram_title_sources,
)
actions = {
"list_jackgram_raw_files": list_jackgram_raw_files,
"list_jackgram_latest_movies": list_jackgram_latest_movies,
"list_jackgram_latest_series": list_jackgram_latest_series,
"list_jackgram_title_sources": list_jackgram_title_sources,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_torrserver(action, params):
if action in ("torrent_action", "torrent_files", "display_picture", "display_text"):
from lib.utils.torrent.torrserver_utils import (
display_picture,
display_text,
torrent_action,
torrent_files,
)
actions = {
"torrent_action": torrent_action,
"torrent_files": torrent_files,
"display_picture": display_picture,
"display_text": display_text,
}
actions[action](params)
elif action in ("download_torrent_subtitles", "download_and_play_subtitles"):
from lib.utils.torrent.torrserver_utils import (
download_and_play_subtitles,
download_torrent_subtitles,
)
actions = {
"download_torrent_subtitles": download_torrent_subtitles,
"download_and_play_subtitles": download_and_play_subtitles,
}
actions[action](params)
elif action == "torrents":
from lib.navigation import torrents
torrents(params)
elif action == "torrentio_selection":
from lib.navigation import torrentio_selection
torrentio_selection(params)
def _route_webdav(action, params):
from lib.clients.webdav.client import (
display_text_webdav,
list_webdav,
show_picture_webdav,
webdav_provider_test,
)
actions = {
"list_webdav": list_webdav,
"webdav_provider_test": webdav_provider_test,
"display_text_webdav": display_text_webdav,
"show_picture_webdav": show_picture_webdav,
}
actions[action](params)
def _route_downloads(action, params):
if action in (
"download_video",
"handle_download_file",
"handle_cancel_download",
"handle_pause_download",
"handle_delete_file",
"resume_download",
"download_cloud_file",
"downloads_viewer",
):
from lib.downloader import (
download_cloud_file,
download_video,
downloads_viewer,
handle_cancel_download,
handle_delete_file,
handle_download_file,
handle_pause_download,
resume_download,
)
actions = {
"download_video": download_video,
"handle_download_file": handle_download_file,
"handle_cancel_download": handle_cancel_download,
"handle_pause_download": handle_pause_download,
"handle_delete_file": handle_delete_file,
"resume_download": resume_download,
"download_cloud_file": download_cloud_file,
"downloads_viewer": downloads_viewer,
}
actions[action](params)
elif action == "downloads_menu":
from lib.navigation import downloads_menu
downloads_menu(params)
elif action == "open_download_manager":
from lib.navigation import open_download_manager
open_download_manager(params)
def _route_mdblist(action, params):
from lib.clients.mdblist.mdblist import (
search_mdbd_lists,
show_mdblist_list,
top_mdbd_lists,
user_mdbd_lists,
)
actions = {
"search_mdbd_lists": search_mdbd_lists,
"user_mdbd_lists": user_mdbd_lists,
"top_mdbd_lists": top_mdbd_lists,
"show_mdblist_list": show_mdblist_list,
}
actions[action](params)
def _route_cache(action, params):
from lib.navigation import (
clear_all_cached,
clear_database_cache,
clear_debrid_cache,
clear_history,
clear_mdblist_cache,
clear_search_history,
clear_stremio_cache,
clear_tmdb_cache,
clear_trakt_cache,
)
actions = {
"clear_all_cached": clear_all_cached,
"clear_trakt_cache": clear_trakt_cache,
"clear_tmdb_cache": clear_tmdb_cache,
"clear_stremio_cache": clear_stremio_cache,
"clear_debrid_cache": clear_debrid_cache,
"clear_mdblist_cache": clear_mdblist_cache,
"clear_database_cache": clear_database_cache,
"clear_history": clear_history,
"clear_search_history": clear_search_history,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_gui(action, params):
if action in ("run_next_dialog", "run_resume_dialog", "run_skip_intro_dialog"):
from lib.gui.custom_dialogs import (
run_next_dialog,
run_resume_dialog,
run_skip_intro_dialog,
)
actions = {
"run_next_dialog": run_next_dialog,
"run_resume_dialog": run_resume_dialog,
"run_skip_intro_dialog": run_skip_intro_dialog,
}
actions[action](params)
elif action == "extras":
from lib.gui.extras_window import ExtrasWindow
from lib.jacktook.utils import ADDON_PATH
# Load the metadata properties needed by the Extras window
item_information = {
"tmdb_id": params.get("id"),
"imdb_id": params.get("imdb_id"),
"media_type": params.get("media_type"),
"title": params.get("title"),
"plot": params.get("plot", ""),
"genre": params.get("genre", ""),
"rating": params.get("rating", ""),
"tv_data": params.get("tv_data", "{}"),
}
xml_file = "extras.xml"
window = ExtrasWindow(xml_file, ADDON_PATH, item_information=item_information)
window.doModal()
del window
else:
from lib.navigation import (
test_download_dialog,
test_resume_dialog,
test_run_next,
test_source_select,
)
actions = {
"test_source_select": test_source_select,
"test_run_next": test_run_next,
"test_resume_dialog": test_resume_dialog,
"test_download_dialog": test_download_dialog,
}
action_func = actions.get(action)
if action_func:
action_func(params)
def _route_source_manager(action, params):
from lib.gui.source_manager_dialog import open_source_manager_dialog
open_source_manager_dialog()
def _route_core(action, params):
if action == "open_excluded_languages_dialog":
from lib.gui.excluded_languages_dialog import open_excluded_languages_dialog
open_excluded_languages_dialog()
return
if action == "choose_external_scraper":
from lib.gui.external_scraper_dialog import choose_external_scraper
choose_external_scraper()
return
if action == "open_external_scraper_settings":
from lib.gui.external_scraper_dialog import open_external_scraper_settings
open_external_scraper_settings()
return
if action in ("resolve_for_pack_selection", "resolve_for_subtitles"):
from lib.actions import resolve_for_pack_selection, resolve_for_subtitles
actions = {
"resolve_for_pack_selection": resolve_for_pack_selection,
"resolve_for_subtitles": resolve_for_subtitles,
}
actions[action](params)
elif action in ("show_episodes_details", "show_seasons_details"):
from lib.utils.views.shows import (
show_episodes_details,
show_seasons_details,
)
actions = {
"show_episodes_details": show_episodes_details,
"show_seasons_details": show_seasons_details,
}
actions[action](params)
elif action == "delete_last_title_entry":
from lib.utils.views.last_titles import delete_last_title_entry
delete_last_title_entry(params)
else:
from lib.navigation import (
add_to_library,
addon_update,
animation_item,
animation_menu,
anime_item,
anime_menu,
anime_search,
choose_view,
clear_library,
continue_watching_menu,
direct_menu,
donate,
downgrade_addon,
easynews_info,
export_settings_backup,
factory_reset,
files_history,
history_menu,
kodi_logs,
library_calendar,
library_menu,
library_movies,
library_shows,
movies_items,
next_page_anime,
open_burst_config,
play_autoscraped,
play_from_pack,
play_media,
play_trailer,
play_url,
remove_from_continue_watching,
remove_from_library,
reset_all_settings,
reset_views,
restore_settings_backup,
save_view,
search,
search_direct,
search_item,
search_menu,
settings,
settings_menu,
show_changelog,
titles_calendar,
titles_history,
trakt_group_menu,
tv_menu,
tv_shows_items,
views_menu,
)
actions = {
"tv_shows_items": tv_shows_items,
"movies_items": movies_items,
"trakt_group_menu": trakt_group_menu,
"direct_menu": direct_menu,
"search_menu": search_menu,
"anime_menu": anime_menu,
"anime_item": anime_item,
"anime_search": anime_search,
"animation_menu": animation_menu,
"animation_item": animation_item,
"tv_menu": tv_menu,
"search": search,
"search_direct": search_direct,
"search_item": search_item,
"next_page_anime": next_page_anime,
"play_media": play_media,
"play_autoscraped": play_autoscraped,
"play_from_pack": play_from_pack,
"play_trailer": play_trailer,
"play_url": play_url,
"settings_menu": settings_menu,
"settings": settings,
"export_settings_backup": export_settings_backup,
"restore_settings_backup": restore_settings_backup,
"reset_all_settings": reset_all_settings,
"factory_reset": factory_reset,
"history_menu": history_menu,
"files_history": files_history,
"titles_history": titles_history,
"titles_calendar": titles_calendar,
"library_menu": library_menu,
"continue_watching_menu": continue_watching_menu,
"library_shows": library_shows,
"library_movies": library_movies,
"library_calendar": library_calendar,
"remove_from_library": remove_from_library,
"clear_library": clear_library,
"remove_continue_watching": remove_from_continue_watching,
"add_to_library": add_to_library,
"donate": donate,
"addon_update": addon_update,
"downgrade_addon": downgrade_addon,
"show_changelog": show_changelog,
"open_burst_config": open_burst_config,
"kodi_logs": kodi_logs,
"easynews_info": easynews_info,
"choose_view": choose_view,
"save_view": save_view,
"reset_views": reset_views,
"views_menu": views_menu,
}
action_func = actions.get(action)
if action_func:
action_func(params)
ROUTE_GROUPS = (
(_is_stremio_action, _route_stremio),
(_is_tmdb_action, _route_tmdb),
(_is_trakt_action, _route_trakt),
(_is_debrid_action, _route_debrid),
(_is_telegram_action, _route_telegram),
(_is_torrserver_action, _route_torrserver),
(_is_webdav_action, _route_webdav),
(_is_download_action, _route_downloads),
(_is_mdblist_action, _route_mdblist),
(_is_cache_action, _route_cache),
(_is_gui_action, _route_gui),
(_is_source_manager_action, _route_source_manager),
)