Files
FastAnime/fastanime/cli/interactive/menus/main.py
2025-07-14 20:09:57 +03:00

157 lines
5.5 KiB
Python

import random
from typing import Callable, Dict, Tuple
from rich.console import Console
from rich.progress import Progress
from ....libs.api.params import ApiSearchParams, UserListParams
from ....libs.api.types import MediaSearchResult, MediaStatus, UserListStatusType
from ..session import Context, session
from ..state import ControlFlow, MediaApiState, State
MenuAction = Callable[[], Tuple[str, MediaSearchResult | None]]
@session.menu
def main(ctx: Context, state: State) -> State | ControlFlow:
"""
The main entry point menu for the interactive session.
Displays top-level categories for the user to browse and select.
"""
icons = ctx.config.general.icons
console = Console()
console.clear()
options: Dict[str, MenuAction] = {
# --- Search-based Actions ---
f"{'🔥 ' if icons else ''}Trending": _create_media_list_action(
ctx, "TRENDING_DESC"
),
f"{'' if icons else ''}Popular": _create_media_list_action(
ctx, "POPULARITY_DESC"
),
f"{'💖 ' if icons else ''}Favourites": _create_media_list_action(
ctx, "FAVOURITES_DESC"
),
f"{'💯 ' if icons else ''}Top Scored": _create_media_list_action(
ctx, "SCORE_DESC"
),
f"{'🎬 ' if icons else ''}Upcoming": _create_media_list_action(
ctx, "POPULARITY_DESC", "NOT_YET_RELEASED"
),
f"{'🔔 ' if icons else ''}Recently Updated": _create_media_list_action(
ctx, "UPDATED_AT_DESC"
),
# --- special case media list --
f"{'🎲 ' if icons else ''}Random": _create_random_media_list(ctx),
f"{'🔎 ' if icons else ''}Search": _create_search_media_list(ctx),
# --- Authenticated User List Actions ---
f"{'📺 ' if icons else ''}Watching": _create_user_list_action(ctx, "CURRENT"),
f"{'📑 ' if icons else ''}Planned": _create_user_list_action(ctx, "PLANNING"),
f"{'' if icons else ''}Completed": _create_user_list_action(
ctx, "COMPLETED"
),
f"{'⏸️ ' if icons else ''}Paused": _create_user_list_action(ctx, "PAUSED"),
f"{'🚮 ' if icons else ''}Dropped": _create_user_list_action(ctx, "DROPPED"),
f"{'🔁 ' if icons else ''}Rewatching": _create_user_list_action(
ctx, "REPEATING"
),
# --- Control Flow and Utility Options ---
f"{'📝 ' if icons else ''}Edit Config": lambda: ("RELOAD_CONFIG", None),
f"{'' if icons else ''}Exit": lambda: ("EXIT", None),
}
choice_str = ctx.selector.choose(
prompt="Select Category",
choices=list(options.keys()),
header="FastAnime Main Menu",
)
if not choice_str:
return ControlFlow.EXIT
# --- Action Handling ---
selected_action = options[choice_str]
next_menu_name, result_data = selected_action()
if next_menu_name == "EXIT":
return ControlFlow.EXIT
if next_menu_name == "RELOAD_CONFIG":
return ControlFlow.RELOAD_CONFIG
if next_menu_name == "CONTINUE":
return ControlFlow.CONTINUE
if not result_data:
console.print(
f"[bold red]Error:[/bold red] Failed to fetch data for '{choice_str.strip()}'."
)
return ControlFlow.CONTINUE
# On success, transition to the RESULTS menu state.
return State(
menu_name="RESULTS",
media_api=MediaApiState(search_results=result_data),
)
def _create_media_list_action(
ctx: Context, sort, status: MediaStatus | None = None
) -> MenuAction:
"""A factory to create menu actions for fetching media lists"""
def action():
with Progress(transient=True) as progress:
progress.add_task(f"[cyan]Fetching anime...", total=None)
return "RESULTS", ctx.media_api.search_media(
ApiSearchParams(
sort=sort, per_page=ctx.config.anilist.per_page, status=status
)
)
return action
def _create_random_media_list(ctx: Context) -> MenuAction:
def action():
with Progress(transient=True) as progress:
progress.add_task(f"[cyan]Fetching random anime...", total=None)
return "RESULTS", ctx.media_api.search_media(
ApiSearchParams(
id_in=random.sample(range(1, 160000), k=50),
per_page=ctx.config.anilist.per_page,
)
)
return action
def _create_search_media_list(ctx: Context) -> MenuAction:
def action():
query = ctx.selector.ask("Search for Anime")
if not query:
return "CONTINUE", None
with Progress(transient=True) as progress:
progress.add_task(f"[cyan]Searching for {query}...", total=None)
return "RESULTS", ctx.media_api.search_media(ApiSearchParams(query=query))
return action
def _create_user_list_action(ctx: Context, status: UserListStatusType) -> MenuAction:
"""A factory to create menu actions for fetching user lists, handling authentication."""
def action():
# if not ctx.media_api.user_profile:
# click.echo(
# f"[bold yellow]Please log in to view your '{status.title()}' list.[/]"
# )
# return "CONTINUE", None
with Progress(transient=True) as progress:
progress.add_task(f"[cyan]Fetching random anime...", total=None)
return "RESULTS", ctx.media_api.fetch_user_list(
UserListParams(status=status, per_page=ctx.config.anilist.per_page)
)
return action