feat(character-preview): attempt to display character image

This commit is contained in:
Benexl
2025-07-29 15:32:21 +03:00
parent ac9b000ce8
commit cd7b70dd6b
4 changed files with 200 additions and 17 deletions

View File

@@ -1,10 +1,7 @@
# fastanime/cli/utils/image.py
from __future__ import annotations
import logging
import shutil
import subprocess
from pathlib import Path
from typing import Optional
import click
@@ -13,6 +10,102 @@ import httpx
logger = logging.getLogger(__name__)
def resize_image_from_url(
client: httpx.Client,
url: str,
new_width: int,
new_height: int,
output_path: Optional[Path] = None,
maintain_aspect_ratio: bool = False,
return_bytes: bool = True,
) -> bytes | None:
"""
Fetches an image from a URL using a provided synchronous httpx.Client,
resizes it with Pillow. Can either save the resized image to a file
or return its bytes.
Args:
client (httpx.Client): An initialized synchronous httpx.Client instance.
url (str): The URL of the image.
new_width (int): The desired new width of the image.
new_height (int): The desired new height of the image.
output_path (str, optional): The path to save the resized image.
Required if return_bytes is False.
maintain_aspect_ratio (bool, optional): If True, resizes while maintaining
the aspect ratio using thumbnail().
Defaults to False.
return_bytes (bool, optional): If True, returns the resized image as bytes.
If False, saves to output_path. Defaults to False.
Returns:
bytes | None: The bytes of the resized image if return_bytes is True,
otherwise None.
"""
from io import BytesIO
from PIL import Image
if not return_bytes and output_path is None:
raise ValueError("output_path must be provided if return_bytes is False.")
try:
# Use the provided synchronous client
response = client.get(url)
response.raise_for_status() # Raise an exception for bad status codes
image_bytes = response.content
image_stream = BytesIO(image_bytes)
img = Image.open(image_stream)
if maintain_aspect_ratio:
img_copy = img.copy()
img_copy.thumbnail((new_width, new_height), Image.Resampling.LANCZOS)
resized_img = img_copy
else:
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
if return_bytes:
# Determine the output format. Default to JPEG if original is unknown or problematic.
# Handle RGBA to RGB conversion for JPEG output.
output_format = (
img.format if img.format in ["JPEG", "PNG", "WEBP"] else "JPEG"
)
if output_format == "JPEG":
if resized_img.mode in ("RGBA", "P"):
resized_img = resized_img.convert("RGB")
byte_arr = BytesIO()
resized_img.save(byte_arr, format=output_format)
logger.info(
f"Image from {url} resized to {resized_img.width}x{resized_img.height} and returned as bytes ({output_format} format)."
)
return byte_arr.getvalue()
else:
# Ensure the directory exists before saving
if output_path:
output_path.parent.mkdir(parents=True, exist_ok=True)
resized_img.save(output_path)
logger.info(
f"Image from {url} resized to {resized_img.width}x{resized_img.height} and saved as '{output_path}'"
)
return None
except httpx.RequestError as e:
logger.error(f"An error occurred while requesting {url}: {e}")
return None
except httpx.HTTPStatusError as e:
logger.error(
f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
)
return None
except ValueError as e:
logger.error(f"Configuration error: {e}")
return None
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None
def render(url: str, capture: bool = False, size: str = "30x30") -> Optional[str]:
"""
Renders an image from a URL in the terminal using icat or chafa.