98 lines
3.1 KiB
Python
98 lines
3.1 KiB
Python
|
|
"""describe-image command."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Annotated, Optional
|
||
|
|
|
||
|
|
import typer
|
||
|
|
|
||
|
|
from freepik_cli.api.client import FreepikAPIError, FreepikClient
|
||
|
|
from freepik_cli.api.images import ImageAPI
|
||
|
|
from freepik_cli.utils.config import FreepikConfig
|
||
|
|
from freepik_cli.utils.console import console, print_describe_result, print_error, print_no_wait
|
||
|
|
from freepik_cli.utils.files import image_to_base64
|
||
|
|
from freepik_cli.utils.polling import FreepikTaskError, FreepikTimeoutError, PollConfig, poll_task
|
||
|
|
|
||
|
|
|
||
|
|
def _get_api_key(api_key: Optional[str], config: FreepikConfig) -> str:
|
||
|
|
key = api_key or config.api_key
|
||
|
|
if not key:
|
||
|
|
print_error("No API key found.", hint="Set [cyan]FREEPIK_API_KEY[/cyan] or pass [cyan]--api-key[/cyan].")
|
||
|
|
raise typer.Exit(1)
|
||
|
|
return key
|
||
|
|
|
||
|
|
|
||
|
|
def describe_image(
|
||
|
|
image: Annotated[
|
||
|
|
Path,
|
||
|
|
typer.Argument(
|
||
|
|
help="Image to analyze and describe",
|
||
|
|
exists=True, file_okay=True, dir_okay=False,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
output: Annotated[
|
||
|
|
Optional[Path],
|
||
|
|
typer.Option("--output", "-o", help="Save the generated prompt to a text file"),
|
||
|
|
] = None,
|
||
|
|
wait: Annotated[
|
||
|
|
bool,
|
||
|
|
typer.Option("--wait/--no-wait"),
|
||
|
|
] = True,
|
||
|
|
api_key: Annotated[
|
||
|
|
Optional[str],
|
||
|
|
typer.Option("--api-key", envvar="FREEPIK_API_KEY"),
|
||
|
|
] = None,
|
||
|
|
) -> None:
|
||
|
|
"""
|
||
|
|
[bold]Describe an image[/bold] and generate a text prompt for it.
|
||
|
|
|
||
|
|
Reverse-engineers the image into an AI-ready prompt you can use with
|
||
|
|
[cyan]generate-image[/cyan].
|
||
|
|
|
||
|
|
[dim]Examples:[/dim]
|
||
|
|
freepik describe-image photo.jpg
|
||
|
|
freepik describe-image scene.png --output prompt.txt
|
||
|
|
"""
|
||
|
|
config = FreepikConfig.load()
|
||
|
|
key = _get_api_key(api_key, config)
|
||
|
|
image_b64 = image_to_base64(image)
|
||
|
|
|
||
|
|
with FreepikClient(key, base_url=config.base_url) as client:
|
||
|
|
api = ImageAPI(client)
|
||
|
|
|
||
|
|
with console.status("[info]Submitting image analysis…[/info]"):
|
||
|
|
try:
|
||
|
|
task_id = api.describe_submit(image_b64)
|
||
|
|
except FreepikAPIError as exc:
|
||
|
|
print_error(str(exc))
|
||
|
|
raise typer.Exit(1)
|
||
|
|
|
||
|
|
if not wait:
|
||
|
|
print_no_wait(task_id, "describe-image", "image-to-prompt")
|
||
|
|
return
|
||
|
|
|
||
|
|
poll_config = PollConfig(task_type="describe", max_wait=config.poll_timeout)
|
||
|
|
try:
|
||
|
|
result = poll_task(
|
||
|
|
check_fn=lambda tid: api.describe_status(tid),
|
||
|
|
task_id=task_id,
|
||
|
|
config=poll_config,
|
||
|
|
console=console,
|
||
|
|
)
|
||
|
|
except (FreepikTaskError, FreepikTimeoutError) as exc:
|
||
|
|
print_error(str(exc))
|
||
|
|
raise typer.Exit(1)
|
||
|
|
|
||
|
|
prompt_text = api.get_prompt_text(result)
|
||
|
|
if not prompt_text:
|
||
|
|
print_error("Analysis completed but no prompt text found.")
|
||
|
|
raise typer.Exit(1)
|
||
|
|
|
||
|
|
saved_path: Optional[Path] = None
|
||
|
|
if output:
|
||
|
|
output.write_text(prompt_text, encoding="utf-8")
|
||
|
|
saved_path = output
|
||
|
|
|
||
|
|
print_describe_result(task_id, prompt_text, saved_path)
|