mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
190 lines
7.3 KiB
Python
190 lines
7.3 KiB
Python
from .text_formatter import TextFormatter
|
|
import os
|
|
|
|
|
|
class CatalogFormatter:
|
|
"""Formatter for catalog mode display"""
|
|
|
|
def __init__(self, extractor, settings=None):
|
|
self.extractor = extractor
|
|
self.settings = settings
|
|
|
|
def format_catalog_info(self) -> str:
|
|
"""Format catalog information for display"""
|
|
lines = []
|
|
poster_output = None
|
|
|
|
# Title
|
|
title = self.extractor.get("title", "TMDB")
|
|
if title:
|
|
lines.append(f"{TextFormatter.bold('Title:')} {title}")
|
|
|
|
# Year
|
|
year = self.extractor.get("year", "TMDB")
|
|
if year:
|
|
lines.append(f"{TextFormatter.bold('Year:')} {year}")
|
|
|
|
# Duration
|
|
duration = self.extractor.get("duration", "TMDB")
|
|
if duration:
|
|
lines.append(f"{TextFormatter.bold('Duration:')} {duration} minutes")
|
|
|
|
# Rates
|
|
popularity = self.extractor.get("popularity", "TMDB")
|
|
vote_average = self.extractor.get("vote_average", "TMDB")
|
|
if popularity or vote_average:
|
|
rates = []
|
|
if popularity:
|
|
rates.append(f"Popularity: {popularity}")
|
|
if vote_average:
|
|
rates.append(f"Rating: {vote_average}/10")
|
|
lines.append(f"{TextFormatter.bold('Rates:')} {', '.join(rates)}")
|
|
|
|
# Overview
|
|
overview = self.extractor.get("overview", "TMDB")
|
|
if overview:
|
|
lines.append(f"{TextFormatter.bold('Overview:')}")
|
|
lines.append(overview)
|
|
|
|
# Genres
|
|
genres = self.extractor.get("genres", "TMDB")
|
|
if genres:
|
|
lines.append(f"{TextFormatter.bold('Genres:')} {genres}")
|
|
|
|
# Countries
|
|
countries = self.extractor.get("production_countries", "TMDB")
|
|
if countries:
|
|
lines.append(f"{TextFormatter.bold('Countries:')} {countries}")
|
|
|
|
# Poster - check settings for display mode
|
|
poster_mode = self.settings.get("poster", "no") if self.settings else "no"
|
|
poster_image_path = self.extractor.tmdb_extractor.extract_poster_image_path()
|
|
|
|
if poster_mode != "no" and poster_image_path:
|
|
lines.append(f"{TextFormatter.bold('Poster:')}")
|
|
poster_output = self._display_poster(poster_image_path, poster_mode)
|
|
elif poster_mode == "no":
|
|
# Don't show poster at all
|
|
poster_output = None
|
|
else:
|
|
# Poster path not cached yet
|
|
poster_path = self.extractor.get("poster_path", "TMDB")
|
|
if poster_path:
|
|
lines.append(f"{TextFormatter.bold('Poster:')} {poster_path} (not cached yet)")
|
|
poster_output = None
|
|
|
|
# Render text content with Rich markup
|
|
text_content = "\n\n".join(lines) if lines else "No catalog information available"
|
|
|
|
from rich.console import Console
|
|
from rich.markup import escape
|
|
from io import StringIO
|
|
|
|
console = Console(file=StringIO(), width=120, legacy_windows=False)
|
|
console.print(text_content, markup=True)
|
|
rendered_text = console.file.getvalue()
|
|
|
|
# Append poster output if available
|
|
# Escape ASCII art to prevent Rich from interpreting characters as markup
|
|
if poster_output:
|
|
# Escape special characters that Rich uses for markup
|
|
escaped_poster = escape(poster_output)
|
|
console2 = Console(file=StringIO(), width=120, legacy_windows=False)
|
|
console2.print(escaped_poster, markup=False)
|
|
return rendered_text + "\n" + console2.file.getvalue()
|
|
else:
|
|
return rendered_text
|
|
|
|
def _display_poster(self, image_path: str, mode: str) -> str:
|
|
"""Display poster image based on mode setting.
|
|
|
|
Args:
|
|
image_path: Path to the poster image
|
|
mode: Display mode - "pseudo" for ASCII art, "viu" for viu rendering
|
|
|
|
Returns:
|
|
Rendered poster as string
|
|
"""
|
|
if not os.path.exists(image_path):
|
|
return f"Image file not found: {image_path}"
|
|
|
|
if mode == "viu":
|
|
return self._display_poster_viu(image_path)
|
|
elif mode == "pseudo":
|
|
return self._display_poster_pseudo(image_path)
|
|
else:
|
|
return f"Unknown poster mode: {mode}"
|
|
|
|
def _display_poster_viu(self, image_path: str) -> str:
|
|
"""Display poster using viu (not working in Textual, only in terminal)"""
|
|
import subprocess
|
|
import shutil
|
|
|
|
# Check if viu is available
|
|
if not shutil.which('viu'):
|
|
return f"viu not installed. Install with: cargo install viu\nPoster at: {image_path}"
|
|
|
|
try:
|
|
# Run viu to render the image
|
|
# -w 40: width in characters
|
|
# -t: transparent background
|
|
result = subprocess.run(
|
|
['viu', '-w', '40', '-t', image_path],
|
|
capture_output=True,
|
|
check=True
|
|
)
|
|
# Decode bytes output, preserving ANSI escape sequences
|
|
return result.stdout.decode('utf-8', errors='replace')
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
stderr_msg = e.stderr.decode('utf-8', errors='replace') if e.stderr else 'Unknown error'
|
|
return f"Failed to render image with viu: {stderr_msg}\nPoster at: {image_path}"
|
|
except Exception as e:
|
|
return f"Failed to display image: {e}\nPoster at: {image_path}"
|
|
|
|
def _display_poster_pseudo(self, image_path: str) -> str:
|
|
"""Display poster image using ASCII art (pseudo graphics)"""
|
|
try:
|
|
from PIL import Image, ImageEnhance
|
|
|
|
# Open image
|
|
img = Image.open(image_path)
|
|
|
|
# Enhance contrast for better detail
|
|
enhancer = ImageEnhance.Contrast(img)
|
|
img = enhancer.enhance(1.3)
|
|
|
|
# Convert to grayscale and resize
|
|
# Compact size for ASCII art (35x35 -> 35x17 after row averaging)
|
|
img = img.convert('L').resize((35, 35), Image.Resampling.LANCZOS)
|
|
|
|
# Extended ASCII characters from darkest to lightest (more gradient levels)
|
|
# Using characters with different visual density for better detail
|
|
ascii_chars = '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,"^`\'. '
|
|
|
|
# Convert to ASCII
|
|
pixels = img.getdata()
|
|
width, height = img.size
|
|
|
|
ascii_art = []
|
|
for y in range(0, height, 2): # Skip every other row for aspect ratio correction
|
|
row = []
|
|
for x in range(width):
|
|
# Average of two rows for better aspect ratio
|
|
pixel1 = pixels[y * width + x] if y < height else 255
|
|
pixel2 = pixels[(y + 1) * width + x] if y + 1 < height else 255
|
|
avg = (pixel1 + pixel2) // 2
|
|
|
|
# Map pixel brightness to character
|
|
# Invert: 0 (black) -> dark char, 255 (white) -> light char
|
|
char_index = (255 - avg) * (len(ascii_chars) - 1) // 255
|
|
char = ascii_chars[char_index]
|
|
row.append(char)
|
|
ascii_art.append(''.join(row))
|
|
|
|
return '\n'.join(ascii_art)
|
|
|
|
except ImportError:
|
|
return f"PIL not available for pseudo graphics\nPoster at: {image_path}"
|
|
except Exception as e:
|
|
return f"Failed to display image: {e}\nPoster at: {image_path}" |