feat: improve preview

This commit is contained in:
Benexl
2025-07-22 18:46:15 +03:00
parent 3092ef0887
commit 43174db8e4
11 changed files with 122 additions and 14 deletions

View File

@@ -30,7 +30,7 @@ def episodes(ctx: Context, state: State) -> State | ControlFlow:
feedback.warning(
f"No '{config.stream.translation_type}' episodes found for this anime."
)
return ControlFlow.BACK
return ControlFlow.BACKX2
chosen_episode: str | None = None
@@ -54,8 +54,8 @@ def episodes(ctx: Context, state: State) -> State | ControlFlow:
)
if not chosen_episode_str or chosen_episode_str == "Back":
# FIX: back broken
return ControlFlow.BACK
# TODO: should improve the back logic for menus that can be pass through
return ControlFlow.BACKX2
chosen_episode = chosen_episode_str

View File

@@ -146,6 +146,15 @@ class Session:
elif next_step == ControlFlow.BACK:
if len(self._history) > 1:
self._history.pop()
elif next_step == ControlFlow.BACKX2:
if len(self._history) > 2:
self._history.pop()
self._history.pop()
elif next_step == ControlFlow.BACKX3:
if len(self._history) > 3:
self._history.pop()
self._history.pop()
self._history.pop()
elif next_step == ControlFlow.CONFIG_EDIT:
self._edit_config()
else:

View File

@@ -24,6 +24,12 @@ class ControlFlow(Enum):
BACK = auto()
"""Pop the current state from history and return to the previous one."""
BACKX2 = auto()
"""Pop x2 the current state from history and return to the previous one."""
BACKX3 = auto()
"""Pop x3 the current state from history and return to the previous one."""
EXIT = auto()
"""Terminate the interactive session gracefully."""

View File

@@ -71,7 +71,7 @@ class MediaFilter:
)
)
or (item.description and query_lower in item.description.lower())
or any(query_lower in syn.lower() for syn in item.synonyms)
or any(query_lower in syn.lower() for syn in item.synonymns)
]
# IDs

View File

@@ -1,4 +1,5 @@
import re
from datetime import datetime
from typing import TYPE_CHECKING, List, Optional
from yt_dlp.utils import clean_html as ytdlp_clean_html
@@ -8,6 +9,24 @@ from ...libs.api.types import AiringSchedule, MediaItem
COMMA_REGEX = re.compile(r"([0-9]{3})(?=\d)")
def format_date(dt: Optional[datetime], format_str: str = "%A, %d %B %Y") -> str:
"""
Formats a datetime object to a readable string.
Default format: '2025-22 July'
Params:
dt (datetime): The datetime object to format.
format_str (str): Optional custom format string (defaults to "%Y-%d %B").
Returns:
str: The formatted date.
"""
if not dt:
return "N/A"
return dt.strftime(format_str)
def clean_html(raw_html: str) -> str:
"""A wrapper around yt-dlp's clean_html to handle None inputs."""
return ytdlp_clean_html(raw_html) if raw_html else ""
@@ -30,9 +49,9 @@ def format_airing_schedule(airing: Optional[AiringSchedule]) -> str:
return f"Ep {airing.episode} on {air_date}"
def format_genres(genres: List[str]) -> str:
def format_list_with_commas(list_of_strs: List[str]) -> str:
"""Joins a list of genres into a single, comma-separated string."""
return ", ".join(genres) if genres else "N/A"
return ", ".join(list_of_strs) if list_of_strs else "N/A"
def format_score_stars_full(score: Optional[float]) -> str:

View File

@@ -72,17 +72,66 @@ def _populate_info_template(item: MediaItem, config: AppConfig) -> str:
# Escape all variables before injecting them into the script
replacements = {
#
# plain text
#
"TITLE": formatters.shell_safe(item.title.english or item.title.romaji),
"STATUS": formatters.shell_safe(item.status),
"FORMAT": formatters.shell_safe(item.format),
#
# numerical
#
"NEXT_EPISODE": formatters.shell_safe(
f"Episode {item.next_airing.episode} on {formatters.format_date(item.next_airing.airing_at)}"
if item.next_airing
else "N/A"
),
"EPISODES": formatters.shell_safe(str(item.episodes)),
"SCORE": formatters.shell_safe(
formatters.format_score_stars_full(item.average_score)
),
"STATUS": formatters.shell_safe(item.status),
"FAVOURITES": formatters.shell_safe(
formatters.format_number_with_commas(item.favourites)
),
"GENRES": formatters.shell_safe(formatters.format_genres(item.genres)),
"POPULARITY": formatters.shell_safe(
formatters.format_number_with_commas(item.popularity)
),
#
# list
#
"GENRES": formatters.shell_safe(
formatters.format_list_with_commas(item.genres)
),
"TAGS": formatters.shell_safe(
formatters.format_list_with_commas([t.name for t in item.tags])
),
"STUDIOS": formatters.shell_safe(
formatters.format_list_with_commas([t.name for t in item.studios if t.name])
),
"SYNONYMNS": formatters.shell_safe(
formatters.format_list_with_commas(item.synonymns)
),
#
# user
#
"USER_STATUS": formatters.shell_safe(
item.user_status.status if item.user_status else "NOT_ON_LIST"
),
"USER_PROGRESS": formatters.shell_safe(
f"Episode {item.user_status.progress}" if item.user_status else "0"
),
#
# dates
#
"START_DATE": formatters.shell_safe(formatters.format_date(item.start_date)),
"END_DATE": formatters.shell_safe(formatters.format_date(item.end_date)),
#
# big guy
#
"SYNOPSIS": formatters.shell_safe(description),
#
# Color codes
#
"C_TITLE": ansi.get_true_fg(HEADER_COLOR, bold=True),
"C_KEY": ansi.get_true_fg(HEADER_COLOR, bold=True),
"C_VALUE": ansi.get_true_fg(HEADER_COLOR, bold=True),
@@ -107,12 +156,12 @@ def _cache_worker(items: List[MediaItem], titles: List[str], config: AppConfig):
_save_image_from_url, item.cover_image.large, hash_id
)
if config.general.preview in ("full", "text"):
if not (INFO_CACHE_DIR / hash_id).exists():
# TODO: Come up with a better caching pattern for now just let it be remade
if not (INFO_CACHE_DIR / hash_id).exists() or True:
info_text = _populate_info_template(item, config)
executor.submit(_save_info_text, info_text, hash_id)
# --- THIS IS THE MODIFIED FUNCTION ---
def get_anime_preview(
items: List[MediaItem], titles: List[str], config: AppConfig
) -> str:
@@ -205,7 +254,7 @@ def _episode_cache_worker(episodes: List[str], anime: MediaItem, config: AppConf
# Find matching streaming episode
episode_data = None
for title, ep in streaming_episodes.items():
if f"Episode {episode_str}" in title or title.endswith(
if f"Episode {episode_str} -" in title or title.endswith(
f" {episode_str}"
):
episode_data = {

View File

@@ -247,7 +247,7 @@ def _to_generic_media_item(
genres=data.get("genres", []),
tags=_to_generic_tags(data.get("tags")),
studios=_to_generic_studios(data.get("studios")),
synonyms=data.get("synonyms", []),
synonymns=data.get("synonyms", []),
average_score=data.get("averageScore"),
popularity=data.get("popularity"),
favourites=data.get("favourites"),

View File

@@ -15,6 +15,7 @@ query (
media {
id
idMal
format
title {
romaji
english

View File

@@ -60,6 +60,7 @@ query (
) {
id
idMal
format
title {
romaji
english

View File

@@ -109,7 +109,7 @@ class MediaItem(BaseApiModel):
genres: List[str] = Field(default_factory=list)
tags: List[MediaTag] = Field(default_factory=list)
studios: List[Studio] = Field(default_factory=list)
synonyms: List[str] = Field(default_factory=list)
synonymns: List[str] = Field(default_factory=list)
average_score: Optional[float] = None
popularity: Optional[int] = None

View File

@@ -52,6 +52,7 @@ draw_rule(){
# --- Display Content ---
draw_rule
print_kv "Title" "{TITLE}"
draw_rule
# Key-Value Stats Section
@@ -60,11 +61,33 @@ if ! [ "{SCORE}" = "N/A" ];then
score_multiplier=2
fi
print_kv "Score" "{SCORE}" $score_multiplier
print_kv "Status" "{STATUS}"
print_kv "Favourites" "{FAVOURITES}"
print_kv "Popularity" "{POPULARITY}"
print_kv "Status" "{STATUS}"
print_kv "Episodes" "{EPISODES}"
print_kv "Next Episode" "{NEXT_EPISODE}"
draw_rule
print_kv "Genres" "{GENRES}"
print_kv "Format" "{FORMAT}"
draw_rule
print_kv "List Status" "{USER_STATUS}"
print_kv "Progress" "{USER_PROGRESS}"
draw_rule
print_kv "Start Date" "{START_DATE}"
print_kv "End Date" "{END_DATE}"
draw_rule
print_kv "Studios" "{STUDIOS}"
print_kv "Synonymns" "{SYNONYMNS}"
print_kv "Tags" "{TAGS}"
draw_rule
# Synopsis