diff --git a/viu_media/assets/scripts/fzf/review_info.py b/viu_media/assets/scripts/fzf/review_info.py index e69de29..fb13de7 100644 --- a/viu_media/assets/scripts/fzf/review_info.py +++ b/viu_media/assets/scripts/fzf/review_info.py @@ -0,0 +1,39 @@ +import sys +from rich.console import Console +from rich.table import Table +from rich.rule import Rule +from rich.markdown import Markdown + +console = Console(force_terminal=True, color_system="truecolor") + +HEADER_COLOR = sys.argv[1] +SEPARATOR_COLOR = sys.argv[2] + + +def rule(title: str | None = None): + console.print(Rule(style=f"rgb({SEPARATOR_COLOR})")) + + +console.print("{REVIEWER_NAME}", justify="center") + +left = [ + ("Summary",), +] +right = [ + ("{REVIEW_SUMMARY}",), +] + + +for L_grp, R_grp in zip(left, right): + table = Table.grid(expand=True) + table.add_column(justify="left", no_wrap=True) + table.add_column(justify="right", overflow="fold") + for L, R in zip(L_grp, R_grp): + table.add_row(f"[bold rgb({HEADER_COLOR})]{L} [/]", f"{R}") + + rule() + console.print(table) + + +rule() +console.print(Markdown("""{REVIEW_BODY}""")) diff --git a/viu_media/cli/utils/preview.py b/viu_media/cli/utils/preview.py index 4077a26..42acd00 100644 --- a/viu_media/cli/utils/preview.py +++ b/viu_media/cli/utils/preview.py @@ -124,12 +124,10 @@ logger = logging.getLogger(__name__) PREVIEWS_CACHE_DIR = APP_CACHE_DIR / "previews" IMAGES_CACHE_DIR = PREVIEWS_CACHE_DIR / "images" INFO_CACHE_DIR = PREVIEWS_CACHE_DIR / "info" -REVIEWS_CACHE_DIR = PREVIEWS_CACHE_DIR / "reviews" AIRING_SCHEDULE_CACHE_DIR = PREVIEWS_CACHE_DIR / "airing_schedule" FZF_SCRIPTS_DIR = SCRIPTS_DIR / "fzf" TEMPLATE_PREVIEW_SCRIPT = (FZF_SCRIPTS_DIR / "preview.py").read_text(encoding="utf-8") -TEMPLATE_REVIEW_PREVIEW_SCRIPT = "" TEMPLATE_AIRING_SCHEDULE_PREVIEW_SCRIPT = "" DYNAMIC_PREVIEW_SCRIPT = "" @@ -417,6 +415,48 @@ def get_character_preview(choice_map: Dict[str, Character], config: AppConfig) - return preview_script +def get_review_preview(choice_map: Dict[str, MediaReview], config: AppConfig) -> str: + """ + Generate the generic loader script for review previews and start background caching. + """ + + IMAGES_CACHE_DIR.mkdir(parents=True, exist_ok=True) + INFO_CACHE_DIR.mkdir(parents=True, exist_ok=True) + + HEADER_COLOR = config.fzf.preview_header_color.split(",") + SEPARATOR_COLOR = config.fzf.preview_separator_color.split(",") + + # Start managed background caching for episodes + try: + preview_manager = _get_preview_manager() + worker = preview_manager.get_review_worker() + worker.cache_review_previews(choice_map, config) + logger.debug("Started background caching for review previews") + except Exception as e: + logger.error(f"Failed to start episode background caching: {e}") + + # Use the generic loader script + preview_script = TEMPLATE_PREVIEW_SCRIPT + + replacements = { + "PREVIEW_MODE": config.general.preview, + "IMAGE_CACHE_DIR": str(IMAGES_CACHE_DIR), + "INFO_CACHE_DIR": str(INFO_CACHE_DIR), + "IMAGE_RENDERER": config.general.image_renderer, + # Color codes + "HEADER_COLOR": ",".join(HEADER_COLOR), + "SEPARATOR_COLOR": ",".join(SEPARATOR_COLOR), + "PREFIX": "review", + "KEY": "", + "SCALE_UP": str(config.general.preview_scale_up), + } + + for key, value in replacements.items(): + preview_script = preview_script.replace(f"{{{key}}}", value) + + return preview_script + + def get_dynamic_anime_preview(config: AppConfig) -> str: """ Generate dynamic anime preview script for search functionality. @@ -475,9 +515,7 @@ def _get_preview_manager() -> PreviewWorkerManager: """Get or create the global preview worker manager.""" global _preview_manager if _preview_manager is None: - _preview_manager = PreviewWorkerManager( - IMAGES_CACHE_DIR, INFO_CACHE_DIR, REVIEWS_CACHE_DIR - ) + _preview_manager = PreviewWorkerManager(IMAGES_CACHE_DIR, INFO_CACHE_DIR) return _preview_manager @@ -503,41 +541,6 @@ def get_preview_worker_status() -> dict: return {"preview_worker": None, "episode_worker": None} -def get_review_preview(choice_map: Dict[str, MediaReview], config: AppConfig) -> str: - """ - Generate the generic loader script for review previews and start background caching. - """ - - REVIEWS_CACHE_DIR.mkdir(parents=True, exist_ok=True) - preview_manager = _get_preview_manager() - worker = preview_manager.get_review_worker() - worker.cache_review_previews(choice_map, config) - logger.debug("Started background caching for review previews") - - # Use the generic loader script - preview_script = TEMPLATE_REVIEW_PREVIEW_SCRIPT - path_sep = "\\" if PLATFORM == "win32" else "/" - - # Inject the correct cache path and color codes - replacements = { - "PREVIEW_MODE": config.general.preview, - "INFO_CACHE_DIR": str(REVIEWS_CACHE_DIR), - "PATH_SEP": path_sep, - "C_TITLE": ansi.get_true_fg(config.fzf.header_color.split(","), bold=True), - "C_KEY": ansi.get_true_fg(config.fzf.header_color.split(","), bold=True), - "C_VALUE": ansi.get_true_fg(config.fzf.header_color.split(","), bold=True), - "C_RULE": ansi.get_true_fg( - config.fzf.preview_separator_color.split(","), bold=True - ), - "RESET": ansi.RESET, - } - - for key, value in replacements.items(): - preview_script = preview_script.replace(f"{{{key}}}", value) - - return preview_script - - def get_airing_schedule_preview( schedule_result: AiringScheduleResult, config: AppConfig, anime_title: str = "Anime" ) -> str: diff --git a/viu_media/cli/utils/preview_workers.py b/viu_media/cli/utils/preview_workers.py index aa2a90b..42ff368 100644 --- a/viu_media/cli/utils/preview_workers.py +++ b/viu_media/cli/utils/preview_workers.py @@ -6,6 +6,7 @@ including image downloads and info text generation with proper lifecycle managem """ import logging +from pathlib import Path from typing import Dict, List, Optional import httpx @@ -421,9 +422,12 @@ class ReviewCacheWorker(ManagedBackgroundWorker): Specialized background worker for caching fully-rendered media review previews. """ - def __init__(self, reviews_cache_dir, max_workers: int = 10): + def __init__( + self, images_cache_dir: Path, info_cache_dir: Path, max_workers: int = 10 + ): super().__init__(max_workers=max_workers, name="ReviewCacheWorker") - self.reviews_cache_dir = reviews_cache_dir + self.images_cache_dir = images_cache_dir + self.info_cache_dir = info_cache_dir def cache_review_previews( self, choice_map: Dict[str, MediaReview], config: AppConfig @@ -471,7 +475,7 @@ class ReviewCacheWorker(ManagedBackgroundWorker): def _save_preview_content(self, content: str, hash_id: str) -> None: """Saves the final preview content to the cache.""" try: - info_path = self.reviews_cache_dir / hash_id + info_path = self.info_cache_dir / hash_id with AtomicWriter(info_path) as f: f.write(content) logger.debug(f"Successfully cached review preview: {hash_id}") @@ -482,7 +486,7 @@ class ReviewCacheWorker(ManagedBackgroundWorker): def _get_cache_hash(self, text: str) -> str: from hashlib import sha256 - return sha256(text.encode("utf-8")).hexdigest() + return "review-" + sha256(text.encode("utf-8")).hexdigest() + ".py" def _on_task_completed(self, task: WorkerTask, future) -> None: super()._on_task_completed(task, future) @@ -757,7 +761,7 @@ class PreviewWorkerManager: caching workers with automatic lifecycle management. """ - def __init__(self, images_cache_dir, info_cache_dir, reviews_cache_dir): + def __init__(self, images_cache_dir, info_cache_dir): """ Initialize the preview worker manager. @@ -768,7 +772,6 @@ class PreviewWorkerManager: """ self.images_cache_dir = images_cache_dir self.info_cache_dir = info_cache_dir - self.reviews_cache_dir = reviews_cache_dir self._preview_worker: Optional[PreviewCacheWorker] = None self._episode_worker: Optional[EpisodeCacheWorker] = None self._review_worker: Optional[ReviewCacheWorker] = None @@ -812,7 +815,9 @@ class PreviewWorkerManager: # Clean up old worker thread_manager.shutdown_worker("review_cache_worker") - self._review_worker = ReviewCacheWorker(self.reviews_cache_dir) + self._review_worker = ReviewCacheWorker( + self.images_cache_dir, self.info_cache_dir + ) self._review_worker.start() thread_manager.register_worker("review_cache_worker", self._review_worker)