feat: implement authentication utilities and integrate with menus

This commit is contained in:
Benexl
2025-07-14 21:07:47 +03:00
parent a079f9919c
commit 064401f8e8
6 changed files with 268 additions and 18 deletions

View File

@@ -6,6 +6,7 @@ from rich.console import Console
from ....libs.api.params import ApiSearchParams, UserListParams
from ....libs.api.types import MediaSearchResult, MediaStatus, UserListStatusType
from ...utils.feedback import create_feedback_manager, execute_with_feedback
from ...utils.auth_utils import format_auth_menu_header, check_authentication_required
from ..session import Context, session
from ..state import ControlFlow, MediaApiState, State
@@ -65,7 +66,7 @@ def main(ctx: Context, state: State) -> State | ControlFlow:
choice_str = ctx.selector.choose(
prompt="Select Category",
choices=list(options.keys()),
header="FastAnime Main Menu",
header=format_auth_menu_header(ctx.media_api, "FastAnime Main Menu", icons),
)
if not choice_str:
@@ -180,13 +181,11 @@ def _create_user_list_action(ctx: Context, status: UserListStatusType) -> MenuAc
def action():
feedback = create_feedback_manager(ctx.config.general.icons)
# Check authentication (commented code from original)
# if not ctx.media_api.user_profile:
# feedback.warning(
# f"Please log in to view your '{status.title()}' list",
# "You need to authenticate with AniList to access your personal lists"
# )
# return "CONTINUE", None
# Check authentication
if not check_authentication_required(
ctx.media_api, feedback, f"view your {status.lower()} list"
):
return "CONTINUE", None
def fetch_data():
return ctx.media_api.fetch_user_list(

View File

@@ -7,6 +7,7 @@ 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
@@ -21,6 +22,14 @@ def media_actions(ctx: Context, state: State) -> State | ControlFlow:
"""
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),
@@ -33,7 +42,7 @@ def media_actions(ctx: Context, state: State) -> State | ControlFlow:
# --- Prompt and Execute ---
choice_str = ctx.selector.choose(
prompt="Select Action", choices=list(options.keys())
prompt="Select Action", choices=list(options.keys()), header=header
)
if choice_str and choice_str in options:
@@ -90,13 +99,21 @@ def _add_to_list(ctx: Context, state: State) -> MenuAction:
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),
UpdateListEntryParams(media_id=anime.id, status=status), # type: ignore
feedback,
)
return ControlFlow.CONTINUE
@@ -110,6 +127,11 @@ def _score_anime(ctx: Context, state: State) -> MenuAction:
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
@@ -180,13 +202,8 @@ 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."""
# Check authentication (commented code from original)
# if not ctx.media_api.user_profile:
# feedback.warning(
# "You must be logged in to modify your list",
# "Please authenticate with AniList to manage your anime lists"
# )
# return
# 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)

View File

@@ -1,6 +1,7 @@
from rich.console import Console
from ....libs.api.types import MediaItem
from ...utils.auth_utils import get_auth_status_indicator
from ..session import Context, session
from ..state import ControlFlow, MediaApiState, State
@@ -47,11 +48,16 @@ def results(ctx: Context, state: State) -> State | ControlFlow:
choices.append("Previous Page")
choices.append("Back")
# Create header with auth status
auth_status, _ = get_auth_status_indicator(ctx.media_api, ctx.config.general.icons)
header = f"Search Results ({len(anime_items)} anime)\n{auth_status}"
# --- Prompt User ---
choice_str = ctx.selector.choose(
prompt="Select Anime",
choices=choices,
preview=preview_command,
header=header,
)
if not choice_str:

View File

@@ -67,15 +67,43 @@ class Session:
from ...libs.providers.anime.provider import create_provider
from ...libs.selectors import create_selector
# Create API client
media_api = create_api_client(config.general.api_client, config)
# Attempt to load saved user authentication
self._load_saved_authentication(media_api)
self._context = Context(
config=config,
provider=create_provider(config.general.provider),
selector=create_selector(config),
player=create_player(config),
media_api=create_api_client(config.general.api_client, config),
media_api=media_api,
)
logger.info("Application context reloaded.")
def _load_saved_authentication(self, media_api):
"""Attempt to load saved user authentication."""
try:
from ..auth.manager import AuthManager
auth_manager = AuthManager()
user_data = auth_manager.load_user_profile()
if user_data and user_data.get("token"):
# Try to authenticate with the saved token
profile = media_api.authenticate(user_data["token"])
if profile:
logger.info(f"Successfully authenticated as {profile.name}")
else:
logger.warning("Saved authentication token is invalid or expired")
else:
logger.debug("No saved authentication found")
except Exception as e:
logger.error(f"Failed to load saved authentication: {e}")
# Continue without authentication rather than failing completely
def _edit_config(self):
"""Handles the logic for editing the config file and reloading the context."""
from ..utils.feedback import create_feedback_manager