Files
valknar 941fd14ccf refactor: rename project from Freepik to Magnific
Rename all identifiers, strings, file names, env vars, CLI entry point,
ASCII banner, and API endpoint to reflect the company rebrand.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:06:16 +02:00

244 lines
8.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""upscale-image and upscale-video commands."""
from __future__ import annotations
from pathlib import Path
from typing import Annotated, Optional
import typer
from magnific_cli.api.client import MagnificAPIError, MagnificClient
from magnific_cli.api.models import UpscaleMode, VideoUpscaleMode
from magnific_cli.api.upscale import UpscaleAPI
from magnific_cli.utils.config import MagnificConfig
from magnific_cli.utils.console import GenerationResult, console, print_error, print_no_wait, print_result
from magnific_cli.utils.files import auto_output_path, get_image_dimensions, image_to_base64, save_from_url, video_to_base64
from magnific_cli.utils.polling import MagnificTaskError, MagnificTimeoutError, PollConfig, poll_task
def _get_api_key(api_key: Optional[str], config: MagnificConfig) -> str:
key = api_key or config.api_key
if not key:
print_error(
"No API key found.",
hint="Set [cyan]MAGNIFIC_API_KEY[/cyan] or pass [cyan]--api-key YOUR_KEY[/cyan].",
)
raise typer.Exit(1)
return key
def upscale_image(
image: Annotated[
Path,
typer.Argument(
help="Image file to upscale",
exists=True, file_okay=True, dir_okay=False,
),
],
mode: Annotated[
UpscaleMode,
typer.Option(
"--mode",
help="Upscaling mode:\n"
" [cyan]precision-v2[/cyan] — faithful, detail-preserving (recommended)\n"
" [cyan]precision[/cyan] — faithful upscaling\n"
" [cyan]creative[/cyan] — AI-enhanced creative reinterpretation",
show_default=True,
),
] = UpscaleMode.PRECISION_V2,
scale: Annotated[
str,
typer.Option("--scale", help="Scale factor: [cyan]2x[/cyan] or [cyan]4x[/cyan]"),
] = "2x",
creativity: Annotated[
Optional[int],
typer.Option(
"--creativity",
help="Creative enhancement level 010 ([cyan]creative[/cyan] mode only)",
min=0, max=10,
),
] = None,
prompt: Annotated[
Optional[str],
typer.Option("--prompt", "-p", help="Enhancement guidance ([cyan]creative[/cyan] mode only)"),
] = None,
seed: Annotated[
Optional[int],
typer.Option("--seed", help="Random seed for reproducibility"),
] = None,
output: Annotated[
Optional[Path],
typer.Option("--output", "-o", help="Output file path"),
] = None,
wait: Annotated[
bool,
typer.Option("--wait/--no-wait", help="Wait for completion or return task ID immediately"),
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY", help="Magnific API key"),
] = None,
) -> None:
"""
[bold]Upscale and enhance an image[/bold] using AI.
[dim]Examples:[/dim]
magnific upscale-image photo.jpg --mode precision-v2 --scale 4x
magnific upscale-image portrait.png --mode creative --creativity 7 --prompt "sharp cinematic"
"""
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
if mode != UpscaleMode.CREATIVE and (creativity is not None or prompt):
print_error(
"--creativity and --prompt are only supported with --mode creative.",
hint="Switch to [cyan]--mode creative[/cyan] or remove those options.",
)
raise typer.Exit(1)
image_b64 = image_to_base64(image)
with MagnificClient(key, base_url=config.base_url) as client:
api = UpscaleAPI(client)
with console.status(f"[info]Submitting {mode.value} upscale ({scale})…[/info]"):
try:
task_id = api.upscale_image(
mode=mode,
image_b64=image_b64,
scale_factor=scale,
creativity=creativity,
prompt=prompt,
seed=seed,
)
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
if not wait:
print_no_wait(task_id, "upscale-image", mode.value)
return
poll_config = PollConfig(task_type="upscale-image", max_wait=config.poll_timeout)
try:
result = poll_task(
check_fn=lambda tid: api.upscale_image_status(mode, tid),
task_id=task_id,
config=poll_config,
console=console,
extra_info={"Mode": mode.value, "Scale": scale},
)
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
urls = api.get_output_urls(result)
if not urls:
print_error("Upscaling completed but no output URL found.")
raise typer.Exit(1)
out = output or auto_output_path("upscaled", mode.value, image.suffix.lstrip(".") or "jpg", config.default_output_dir)
save_from_url(urls[0], out, console)
w, h = get_image_dimensions(out)
print_result(
GenerationResult(
task_id=task_id,
model=mode.value,
output_path=out,
width=w,
height=h,
task_type="upscale-image",
)
)
def upscale_video(
video: Annotated[
Path,
typer.Argument(
help="Video file to upscale",
exists=True, file_okay=True, dir_okay=False,
),
],
mode: Annotated[
VideoUpscaleMode,
typer.Option(
"--mode",
help="Upscaling mode: [cyan]standard[/cyan] | [cyan]turbo[/cyan] (faster)",
show_default=True,
),
] = VideoUpscaleMode.STANDARD,
output: Annotated[
Optional[Path],
typer.Option("--output", "-o", help="Output video file path"),
] = None,
wait: Annotated[
bool,
typer.Option("--wait/--no-wait"),
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY"),
] = None,
) -> None:
"""
[bold]Upscale a video[/bold] to higher resolution using AI.
[dim]Examples:[/dim]
magnific upscale-video clip.mp4 --mode standard
magnific upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
"""
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
video_b64 = video_to_base64(video)
with MagnificClient(key, base_url=config.base_url) as client:
api = UpscaleAPI(client)
with console.status(f"[info]Submitting video upscale ({mode.value})…[/info]"):
try:
task_id = api.upscale_video(mode=mode, video_b64=video_b64)
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
if not wait:
print_no_wait(task_id, "upscale-video", mode.value)
return
poll_config = PollConfig(
task_type="upscale-video",
initial_delay=5.0,
max_wait=config.poll_timeout,
)
try:
result = poll_task(
check_fn=lambda tid: api.upscale_video_status(tid),
task_id=task_id,
config=poll_config,
console=console,
extra_info={"Mode": mode.value},
)
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
urls = api.get_output_urls(result)
if not urls:
print_error("Upscaling completed but no output URL found.")
raise typer.Exit(1)
out = output or auto_output_path("upscaled_video", mode.value, "mp4", config.default_output_dir)
save_from_url(urls[0], out, console)
print_result(
GenerationResult(
task_id=task_id,
model=mode.value,
output_path=out,
task_type="upscale-video",
)
)