mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-06 09:43:53 -08:00
221 lines
7.7 KiB
Python
221 lines
7.7 KiB
Python
from typing import Callable, Dict
|
||
|
||
import click
|
||
from rich.console import Console
|
||
|
||
from ....libs.api.params import UpdateListEntryParams
|
||
from ....libs.api.types import MediaItem
|
||
from ....libs.players.params import PlayerParams
|
||
from ...utils.feedback import create_feedback_manager, execute_with_feedback
|
||
from ...utils.auth_utils import check_authentication_required, get_auth_status_indicator
|
||
from ..session import Context, session
|
||
from ..state import ControlFlow, ProviderState, State
|
||
|
||
MenuAction = Callable[[], State | ControlFlow]
|
||
|
||
|
||
@session.menu
|
||
def media_actions(ctx: Context, state: State) -> State | ControlFlow:
|
||
"""
|
||
Displays actions for a single, selected anime, such as streaming,
|
||
viewing details, or managing its status on the user's list.
|
||
"""
|
||
icons = ctx.config.general.icons
|
||
|
||
# Get authentication status for display
|
||
auth_status, user_profile = get_auth_status_indicator(ctx.media_api, icons)
|
||
|
||
# Create header with auth status
|
||
anime = state.media_api.anime
|
||
anime_title = anime.title.english or anime.title.romaji if anime else "Unknown"
|
||
header = f"Actions for: {anime_title}\n{auth_status}"
|
||
|
||
# TODO: Add 'Recommendations' and 'Relations' here later.
|
||
options: Dict[str, MenuAction] = {
|
||
f"{'▶️ ' if icons else ''}Stream": _stream(ctx, state),
|
||
f"{'📼 ' if icons else ''}Watch Trailer": _watch_trailer(ctx, state),
|
||
f"{'➕ ' if icons else ''}Add/Update List": _add_to_list(ctx, state),
|
||
f"{'⭐ ' if icons else ''}Score Anime": _score_anime(ctx, state),
|
||
f"{'ℹ️ ' if icons else ''}View Info": _view_info(ctx, state),
|
||
f"{'🔙 ' if icons else ''}Back to Results": lambda: ControlFlow.BACK,
|
||
}
|
||
|
||
# --- Prompt and Execute ---
|
||
choice_str = ctx.selector.choose(
|
||
prompt="Select Action", choices=list(options.keys()), header=header
|
||
)
|
||
|
||
if choice_str and choice_str in options:
|
||
return options[choice_str]()
|
||
|
||
return ControlFlow.BACK
|
||
|
||
|
||
# --- Action Implementations ---
|
||
def _stream(ctx: Context, state: State) -> MenuAction:
|
||
def action():
|
||
return State(
|
||
menu_name="PROVIDER_SEARCH",
|
||
media_api=state.media_api, # Carry over the existing api state
|
||
provider=ProviderState(), # Initialize a fresh provider state
|
||
)
|
||
|
||
return action
|
||
|
||
|
||
def _watch_trailer(ctx: Context, state: State) -> MenuAction:
|
||
def action():
|
||
feedback = create_feedback_manager(ctx.config.general.icons)
|
||
anime = state.media_api.anime
|
||
if not anime:
|
||
return ControlFlow.CONTINUE
|
||
if not anime.trailer or not anime.trailer.id:
|
||
feedback.warning(
|
||
"No trailer available for this anime",
|
||
"This anime doesn't have a trailer link in the database",
|
||
)
|
||
else:
|
||
trailer_url = f"https://www.youtube.com/watch?v={anime.trailer.id}"
|
||
|
||
def play_trailer():
|
||
ctx.player.play(PlayerParams(url=trailer_url, title=""))
|
||
|
||
execute_with_feedback(
|
||
play_trailer,
|
||
feedback,
|
||
"play trailer",
|
||
loading_msg=f"Playing trailer for '{anime.title.english or anime.title.romaji}'",
|
||
success_msg="Trailer started successfully",
|
||
show_loading=False,
|
||
)
|
||
return ControlFlow.CONTINUE
|
||
|
||
return action
|
||
|
||
|
||
def _add_to_list(ctx: Context, state: State) -> MenuAction:
|
||
def action():
|
||
feedback = create_feedback_manager(ctx.config.general.icons)
|
||
anime = state.media_api.anime
|
||
if not anime:
|
||
return ControlFlow.CONTINUE
|
||
|
||
# Check authentication before proceeding
|
||
if not check_authentication_required(
|
||
ctx.media_api, feedback, "add anime to your list"
|
||
):
|
||
return ControlFlow.CONTINUE
|
||
|
||
choices = ["CURRENT", "PLANNING", "COMPLETED", "DROPPED", "PAUSED", "REPEATING"]
|
||
status = ctx.selector.choose("Select list status:", choices=choices)
|
||
if status:
|
||
# status is now guaranteed to be one of the valid choices
|
||
_update_user_list_with_feedback(
|
||
ctx,
|
||
anime,
|
||
UpdateListEntryParams(media_id=anime.id, status=status), # type: ignore
|
||
feedback,
|
||
)
|
||
return ControlFlow.CONTINUE
|
||
|
||
return action
|
||
|
||
|
||
def _score_anime(ctx: Context, state: State) -> MenuAction:
|
||
def action():
|
||
feedback = create_feedback_manager(ctx.config.general.icons)
|
||
anime = state.media_api.anime
|
||
if not anime:
|
||
return ControlFlow.CONTINUE
|
||
|
||
# Check authentication before proceeding
|
||
if not check_authentication_required(ctx.media_api, feedback, "score anime"):
|
||
return ControlFlow.CONTINUE
|
||
|
||
score_str = ctx.selector.ask("Enter score (0.0 - 10.0):")
|
||
try:
|
||
score = float(score_str) if score_str else 0.0
|
||
if not 0.0 <= score <= 10.0:
|
||
raise ValueError("Score out of range.")
|
||
_update_user_list_with_feedback(
|
||
ctx,
|
||
anime,
|
||
UpdateListEntryParams(media_id=anime.id, score=score),
|
||
feedback,
|
||
)
|
||
except (ValueError, TypeError):
|
||
feedback.error(
|
||
"Invalid score entered", "Please enter a number between 0.0 and 10.0"
|
||
)
|
||
return ControlFlow.CONTINUE
|
||
|
||
return action
|
||
|
||
|
||
def _view_info(ctx: Context, state: State) -> MenuAction:
|
||
def action():
|
||
anime = state.media_api.anime
|
||
if not anime:
|
||
return ControlFlow.CONTINUE
|
||
# Placeholder for a more detailed info screen if needed.
|
||
# For now, we'll just print key details.
|
||
from rich import box
|
||
from rich.panel import Panel
|
||
from rich.text import Text
|
||
|
||
from ...utils import image
|
||
|
||
console = Console()
|
||
title = Text(anime.title.english or anime.title.romaji or "", style="bold cyan")
|
||
description = Text(anime.description or "NO description")
|
||
genres = Text(f"Genres: {', '.join(anime.genres)}")
|
||
|
||
panel_content = f"{genres}\n\n{description}"
|
||
|
||
console.clear()
|
||
if cover_image := anime.cover_image:
|
||
image.render_image(cover_image.large)
|
||
|
||
console.print(Panel(panel_content, title=title, box=box.ROUNDED, expand=True))
|
||
ctx.selector.ask("Press Enter to continue...")
|
||
return ControlFlow.CONTINUE
|
||
|
||
return action
|
||
|
||
|
||
def _update_user_list(ctx: Context, anime: MediaItem, params: UpdateListEntryParams):
|
||
"""Helper to call the API to update a user's list and show feedback."""
|
||
# if not ctx.media_api.user_profile:
|
||
# click.echo("[bold yellow]You must be logged in to modify your list.[/]")
|
||
# return
|
||
|
||
success = ctx.media_api.update_list_entry(params)
|
||
if success:
|
||
click.echo(
|
||
f"[bold green]Successfully updated '{anime.title.english or anime.title.romaji}' on your list![/]"
|
||
)
|
||
else:
|
||
click.echo("[bold red]Failed to update list entry.[/bold red]")
|
||
|
||
|
||
def _update_user_list_with_feedback(
|
||
ctx: Context, anime: MediaItem, params: UpdateListEntryParams, feedback
|
||
):
|
||
"""Helper to call the API to update a user's list with comprehensive feedback."""
|
||
# Authentication check is handled by the calling functions now
|
||
# This function assumes authentication has already been verified
|
||
|
||
def update_operation():
|
||
return ctx.media_api.update_list_entry(params)
|
||
|
||
anime_title = anime.title.english or anime.title.romaji
|
||
success, result = execute_with_feedback(
|
||
update_operation,
|
||
feedback,
|
||
"update anime list",
|
||
loading_msg=f"Updating '{anime_title}' on your list",
|
||
success_msg=f"Successfully updated '{anime_title}' on your list!",
|
||
error_msg="Failed to update list entry",
|
||
show_loading=False,
|
||
)
|