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>
This commit is contained in:
2026-05-16 13:06:16 +02:00
parent e013db9065
commit 941fd14ccf
24 changed files with 294 additions and 294 deletions
+9 -9
View File
@@ -1,11 +1,11 @@
# Freepik API Configuration
# Get your API key at https://www.freepik.com/api
FREEPIK_API_KEY=your_api_key_here
# Magnific API Configuration
# Get your API key at https://magnific.ai
MAGNIFIC_API_KEY=your_api_key_here
# Optional overrides
# FREEPIK_BASE_URL=https://api.freepik.com
# FREEPIK_POLL_TIMEOUT=600
# FREEPIK_DEFAULT_IMAGE_MODEL=flux-2-pro
# FREEPIK_DEFAULT_VIDEO_MODEL=kling-o1-pro
# FREEPIK_DEFAULT_OUTPUT_DIR=./output
# FREEPIK_SHOW_BANNER=true
# MAGNIFIC_BASE_URL=https://api.magnific.ai
# MAGNIFIC_POLL_TIMEOUT=600
# MAGNIFIC_DEFAULT_IMAGE_MODEL=flux-2-pro
# MAGNIFIC_DEFAULT_VIDEO_MODEL=kling-o1-pro
# MAGNIFIC_DEFAULT_OUTPUT_DIR=./output
# MAGNIFIC_SHOW_BANNER=true
+88 -88
View File
@@ -1,15 +1,15 @@
# Freepik AI CLI
# Magnific AI CLI
A sophisticated, beautiful command-line interface for the [Freepik AI API](https://docs.freepik.com/introduction) — generate images, animate videos, upscale media, and more, all from your terminal.
A sophisticated, beautiful command-line interface for the [Magnific AI API](https://magnific.ai) — generate images, animate videos, upscale media, and more, all from your terminal.
```
██████╗██████╗ ███████╗███████╗██████╗ ██╗██╗ ██╗
██╔════╝██╔══██╗██╔════╝██╔════╝██╔══████║██║ ██╔╝
█████╗ ██████╔╝█████ █████╗ ██████╔╝██║█████╔╝
██╔══╝ ██╔══████╔══╝ ██╔══╝ ██╔══╝ ██║██╔═██╗
██║ ██║ ██║███████╗█████████║ ██║████╗
╚═╝ ╚═╝ ╚═╝╚═════╝╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝
AI Media Generation CLI • v0.1.0
███╗ ███╗ █████╗ ██████╗ ███████╗███████╗██╗ ██████╗
████╗ ████║██╔══██╗██╔════╝ ████╗ ████║██╔════╝██║██╔════
██████╔██║█████████ ███╗██╔██╗ █████████╗ ████
██║╚██╔╝██║██╔══████║ ██║██║╚██╗██║██║██╔══╝ ██║██
██║ ╚═╝ ██║██║ ██║██████╔╝██║ ╚████████║ ██║██████╗
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚═════╝
AI Media Generation CLI • v0.1.0
```
## Features
@@ -18,20 +18,20 @@ A sophisticated, beautiful command-line interface for the [Freepik AI API](https
- **Beautiful terminal UI** — live polling panels, progress bars, color-coded status, Rich-themed output
- **15+ AI models** — Flux, Mystic, Seedream, Kling, Minimax, Runway, and more
- **Async-first** — all long-running tasks poll with exponential backoff; use `--no-wait` for fire-and-forget
- **Zero config required** — just set `FREEPIK_API_KEY` and go
- **Zero config required** — just set `MAGNIFIC_API_KEY` and go
## Installation
### Prerequisites
- Python 3.11+
- A [Freepik API key](https://www.freepik.com/api) (free tier includes $5 credit)
- A [Magnific API key](https://magnific.ai) (free tier includes $5 credit)
### Setup
```bash
git clone <repo>
cd freepik
cd magnific
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
@@ -42,7 +42,7 @@ pip install -e .
### API Key
```bash
export FREEPIK_API_KEY=your_api_key_here
export MAGNIFIC_API_KEY=your_api_key_here
```
Or add it to a `.env` file in the project root (see `.env.example`).
@@ -51,19 +51,19 @@ Or add it to a `.env` file in the project root (see `.env.example`).
```bash
# Generate an image
freepik generate-image "a misty forest at dawn, cinematic lighting"
magnific generate-image "a misty forest at dawn, cinematic lighting"
# Animate an image into a video
freepik generate-video photo.jpg --prompt "gentle camera drift" --duration 5
magnific generate-video photo.jpg --prompt "gentle camera drift" --duration 5
# Upscale an image 4x
freepik upscale-image photo.jpg --mode precision-v2 --scale 4x
magnific upscale-image photo.jpg --mode precision-v2 --scale 4x
# Upscale a video
freepik upscale-video clip.mp4 --mode turbo
magnific upscale-video clip.mp4 --mode turbo
# Describe an image (reverse-engineer its prompt)
freepik describe-image painting.jpg
magnific describe-image painting.jpg
```
## Commands
@@ -73,7 +73,7 @@ freepik describe-image painting.jpg
Generate an image from a text prompt.
```bash
freepik generate-image <prompt> [OPTIONS]
magnific generate-image <prompt> [OPTIONS]
```
| Option | Short | Default | Description |
@@ -85,14 +85,14 @@ freepik generate-image <prompt> [OPTIONS]
| `--input-image` | `-i` | — | Reference image for img2img (flux-kontext-pro) |
| `--output` | `-o` | auto | Output file path |
| `--wait / --no-wait` | | `--wait` | Wait for completion or return task ID |
| `--api-key` | | `$FREEPIK_API_KEY` | API key override |
| `--api-key` | | `$MAGNIFIC_API_KEY` | API key override |
**Available models:**
| Model | Description |
|-------|-------------|
| `flux-2-pro` | High-quality, versatile (default) |
| `mystic` | Freepik's exclusive photorealistic workflow |
| `mystic` | Magnific's exclusive photorealistic workflow |
| `flux-kontext-pro` | Instruction-based image editing (img2img) |
| `flux-2-turbo` | Fast generation |
| `flux-pro-1.1` | Flux Pro v1.1 |
@@ -103,11 +103,11 @@ freepik generate-image <prompt> [OPTIONS]
**Examples:**
```bash
freepik generate-image "a cat on the moon, oil painting style"
freepik generate-image "cyberpunk city at night" --model mystic --aspect-ratio 16:9
freepik generate-image "make the sky orange at sunset" --model flux-kontext-pro --input-image photo.jpg
freepik generate-image "portrait of a wizard" --seed 42 --output wizard.jpg
freepik generate-image "vast ocean panorama" --model flux-2-pro --no-wait
magnific generate-image "a cat on the moon, oil painting style"
magnific generate-image "cyberpunk city at night" --model mystic --aspect-ratio 16:9
magnific generate-image "make the sky orange at sunset" --model flux-kontext-pro --input-image photo.jpg
magnific generate-image "portrait of a wizard" --seed 42 --output wizard.jpg
magnific generate-image "vast ocean panorama" --model flux-2-pro --no-wait
```
---
@@ -117,7 +117,7 @@ freepik generate-image "vast ocean panorama" --model flux-2-pro --no-wait
Animate a source image into a short video clip.
```bash
freepik generate-video <image> [OPTIONS]
magnific generate-video <image> [OPTIONS]
```
| Option | Short | Default | Description |
@@ -129,7 +129,7 @@ freepik generate-video <image> [OPTIONS]
| `--seed` | | — | Seed for reproducibility |
| `--output` | `-o` | auto | Output `.mp4` path |
| `--wait / --no-wait` | | `--wait` | |
| `--api-key` | | `$FREEPIK_API_KEY` | |
| `--api-key` | | `$MAGNIFIC_API_KEY` | |
**Available models:**
@@ -146,9 +146,9 @@ freepik generate-video <image> [OPTIONS]
**Examples:**
```bash
freepik generate-video photo.jpg --prompt "gentle ocean waves lapping at the shore"
freepik generate-video portrait.png --model minimax-hailuo --duration 10 --aspect-ratio 9:16
freepik generate-video landscape.jpg --model kling-o1-pro --output timelapse.mp4
magnific generate-video photo.jpg --prompt "gentle ocean waves lapping at the shore"
magnific generate-video portrait.png --model minimax-hailuo --duration 10 --aspect-ratio 9:16
magnific generate-video landscape.jpg --model kling-o1-pro --output timelapse.mp4
```
---
@@ -158,7 +158,7 @@ freepik generate-video landscape.jpg --model kling-o1-pro --output timelapse.mp4
Upscale and enhance an image using AI.
```bash
freepik upscale-image <image> [OPTIONS]
magnific upscale-image <image> [OPTIONS]
```
| Option | Short | Default | Description |
@@ -170,7 +170,7 @@ freepik upscale-image <image> [OPTIONS]
| `--seed` | | — | Seed for reproducibility |
| `--output` | `-o` | auto | Output file path |
| `--wait / --no-wait` | | `--wait` | |
| `--api-key` | | `$FREEPIK_API_KEY` | |
| `--api-key` | | `$MAGNIFIC_API_KEY` | |
**Modes:**
@@ -183,9 +183,9 @@ freepik upscale-image <image> [OPTIONS]
**Examples:**
```bash
freepik upscale-image photo.jpg --scale 4x
freepik upscale-image photo.jpg --mode precision-v2 --scale 2x --output photo_hd.jpg
freepik upscale-image portrait.jpg --mode creative --creativity 6 --prompt "sharp cinematic texture"
magnific upscale-image photo.jpg --scale 4x
magnific upscale-image photo.jpg --mode precision-v2 --scale 2x --output photo_hd.jpg
magnific upscale-image portrait.jpg --mode creative --creativity 6 --prompt "sharp cinematic texture"
```
---
@@ -195,7 +195,7 @@ freepik upscale-image portrait.jpg --mode creative --creativity 6 --prompt "shar
Upscale a video to higher resolution using AI.
```bash
freepik upscale-video <video> [OPTIONS]
magnific upscale-video <video> [OPTIONS]
```
| Option | Short | Default | Description |
@@ -203,13 +203,13 @@ freepik upscale-video <video> [OPTIONS]
| `--mode` | | `standard` | `standard` \| `turbo` (faster) |
| `--output` | `-o` | auto | Output `.mp4` path |
| `--wait / --no-wait` | | `--wait` | |
| `--api-key` | | `$FREEPIK_API_KEY` | |
| `--api-key` | | `$MAGNIFIC_API_KEY` | |
**Examples:**
```bash
freepik upscale-video clip.mp4
freepik upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
magnific upscale-video clip.mp4
magnific upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
```
---
@@ -219,7 +219,7 @@ freepik upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
Generate an icon from a text prompt in various styles.
```bash
freepik generate-icon <prompt> [OPTIONS]
magnific generate-icon <prompt> [OPTIONS]
```
| Option | Short | Default | Description |
@@ -234,9 +234,9 @@ freepik generate-icon <prompt> [OPTIONS]
**Examples:**
```bash
freepik generate-icon "shopping cart" --style solid --format svg
freepik generate-icon "rocket ship" --style color --format png
freepik generate-icon "leaf" --style outline --format svg --output leaf.svg
magnific generate-icon "shopping cart" --style solid --format svg
magnific generate-icon "rocket ship" --style color --format png
magnific generate-icon "leaf" --style outline --format svg --output leaf.svg
```
---
@@ -246,7 +246,7 @@ freepik generate-icon "leaf" --style outline --format svg --output leaf.svg
Expand an image by generating new content around its edges (outpainting).
```bash
freepik expand-image <image> [OPTIONS]
magnific expand-image <image> [OPTIONS]
```
| Option | Default | Description |
@@ -263,9 +263,9 @@ freepik expand-image <image> [OPTIONS]
**Examples:**
```bash
freepik expand-image photo.jpg --left 512 --right 512 --prompt "lush green forest"
freepik expand-image banner.png --bottom 256 --model seedream-v4-5
freepik expand-image portrait.jpg --top 300 --bottom 300 --prompt "studio backdrop"
magnific expand-image photo.jpg --left 512 --right 512 --prompt "lush green forest"
magnific expand-image banner.png --bottom 256 --model seedream-v4-5
magnific expand-image portrait.jpg --top 300 --bottom 300 --prompt "studio backdrop"
```
---
@@ -275,7 +275,7 @@ freepik expand-image portrait.jpg --top 300 --bottom 300 --prompt "studio backdr
Analyze an image and generate a descriptive text prompt for it — useful for reverse-engineering AI images or building prompt libraries.
```bash
freepik describe-image <image> [OPTIONS]
magnific describe-image <image> [OPTIONS]
```
| Option | Short | Description |
@@ -287,8 +287,8 @@ freepik describe-image <image> [OPTIONS]
**Examples:**
```bash
freepik describe-image painting.jpg
freepik describe-image scene.png --output scene_prompt.txt
magnific describe-image painting.jpg
magnific describe-image scene.png --output scene_prompt.txt
```
---
@@ -298,7 +298,7 @@ freepik describe-image scene.png --output scene_prompt.txt
Relight an image using AI-controlled lighting.
```bash
freepik relight <image> [OPTIONS]
magnific relight <image> [OPTIONS]
```
| Option | Short | Description |
@@ -310,8 +310,8 @@ freepik relight <image> [OPTIONS]
**Examples:**
```bash
freepik relight portrait.jpg --prompt "warm golden hour sunlight from the left"
freepik relight product.png --prompt "soft studio lighting, white background"
magnific relight portrait.jpg --prompt "warm golden hour sunlight from the left"
magnific relight product.png --prompt "soft studio lighting, white background"
```
---
@@ -321,7 +321,7 @@ freepik relight product.png --prompt "soft studio lighting, white background"
Apply the artistic style from one image onto the content of another.
```bash
freepik style-transfer <content-image> <style-image> [OPTIONS]
magnific style-transfer <content-image> <style-image> [OPTIONS]
```
| Option | Description |
@@ -332,23 +332,23 @@ freepik style-transfer <content-image> <style-image> [OPTIONS]
**Examples:**
```bash
freepik style-transfer photo.jpg van_gogh.jpg
freepik style-transfer portrait.png impressionist.jpg --strength 0.75 --output styled.jpg
magnific style-transfer photo.jpg van_gogh.jpg
magnific style-transfer portrait.png impressionist.jpg --strength 0.75 --output styled.jpg
```
---
### `config`
Manage CLI configuration stored at `~/.config/freepik-cli/config.toml`.
Manage CLI configuration stored at `~/.config/magnific-cli/config.toml`.
```bash
freepik config show # Display all settings (as a table)
freepik config show --toml # Display as highlighted TOML
freepik config get <key> # Print a single value
freepik config set <key> <value> # Update a setting
freepik config reset # Reset to defaults
freepik config path # Print the config file path
magnific config show # Display all settings (as a table)
magnific config show --toml # Display as highlighted TOML
magnific config get <key> # Print a single value
magnific config set <key> <value> # Update a setting
magnific config reset # Reset to defaults
magnific config path # Print the config file path
```
**Configurable keys:**
@@ -359,20 +359,20 @@ freepik config path # Print the config file path
| `default_video_model` | `kling-o1-pro` | Default model for `generate-video` |
| `default_upscale_mode` | `precision-v2` | Default mode for `upscale-image` |
| `default_output_dir` | `.` | Directory for auto-generated output files |
| `base_url` | `https://api.freepik.com` | API base URL |
| `base_url` | `https://api.magnific.ai` | API base URL |
| `poll_timeout` | `600` | Max seconds to wait for task completion |
| `poll_max_interval` | `15` | Max seconds between polling attempts |
| `show_banner` | `true` | Show the ASCII art banner |
> **Note:** The API key is never saved to the config file. Use `FREEPIK_API_KEY` or `--api-key`.
> **Note:** The API key is never saved to the config file. Use `MAGNIFIC_API_KEY` or `--api-key`.
**Examples:**
```bash
freepik config set default_image_model mystic
freepik config set default_output_dir ~/Pictures/freepik
freepik config set show_banner false
freepik config set poll_timeout 300
magnific config set default_image_model mystic
magnific config set default_output_dir ~/Pictures/magnific
magnific config set show_banner false
magnific config set poll_timeout 300
```
## Configuration
@@ -380,36 +380,36 @@ freepik config set poll_timeout 300
Settings are resolved in this priority order (highest wins):
1. `--api-key` / `--model` / etc. command-line flags
2. `FREEPIK_*` environment variables (e.g. `FREEPIK_API_KEY`)
3. `~/.config/freepik-cli/config.toml`
2. `MAGNIFIC_*` environment variables (e.g. `MAGNIFIC_API_KEY`)
3. `~/.config/magnific-cli/config.toml`
4. Built-in defaults
### Environment Variables
| Variable | Description |
|----------|-------------|
| `FREEPIK_API_KEY` | Your Freepik API key **(required)** |
| `FREEPIK_BASE_URL` | API base URL override |
| `FREEPIK_DEFAULT_IMAGE_MODEL` | Default image model |
| `FREEPIK_DEFAULT_VIDEO_MODEL` | Default video model |
| `FREEPIK_DEFAULT_OUTPUT_DIR` | Default output directory |
| `FREEPIK_POLL_TIMEOUT` | Task polling timeout in seconds |
| `FREEPIK_SHOW_BANNER` | Show/hide the ASCII banner (`true`/`false`) |
| `MAGNIFIC_API_KEY` | Your Magnific API key **(required)** |
| `MAGNIFIC_BASE_URL` | API base URL override |
| `MAGNIFIC_DEFAULT_IMAGE_MODEL` | Default image model |
| `MAGNIFIC_DEFAULT_VIDEO_MODEL` | Default video model |
| `MAGNIFIC_DEFAULT_OUTPUT_DIR` | Default output directory |
| `MAGNIFIC_POLL_TIMEOUT` | Task polling timeout in seconds |
| `MAGNIFIC_SHOW_BANNER` | Show/hide the ASCII banner (`true`/`false`) |
## Output Files
When no `--output` path is provided, files are saved with an auto-generated name:
```
freepik_image_flux-2-pro_20260408_143022.jpg
freepik_video_kling-o1-pro_20260408_143512.mp4
freepik_upscaled_precision-v2_20260408_144001.jpg
magnific_image_flux-2-pro_20260408_143022.jpg
magnific_video_kling-o1-pro_20260408_143512.mp4
magnific_upscaled_precision-v2_20260408_144001.jpg
```
The default output directory is the current working directory. Change it with:
```bash
freepik config set default_output_dir ~/Pictures/freepik
magnific config set default_output_dir ~/Pictures/magnific
```
## Async Workflows (`--no-wait`)
@@ -418,7 +418,7 @@ Every command supports `--no-wait` to submit a task and return immediately witho
```bash
# Submit and get task ID instantly
freepik generate-image "a nebula" --model mystic --no-wait
magnific generate-image "a nebula" --model mystic --no-wait
# Task Queued
# Task ID: c3f2a1b8-...
@@ -433,7 +433,7 @@ This is useful for batching multiple requests or integrating with scripts.
Install tab-completion for your shell:
```bash
freepik --install-completion # auto-detects your shell
magnific --install-completion # auto-detects your shell
```
Supports bash, zsh, fish, and PowerShell.
@@ -441,10 +441,10 @@ Supports bash, zsh, fish, and PowerShell.
## Project Structure
```
freepik/
magnific/
├── pyproject.toml
├── .env.example
├── freepik_cli/
├── magnific_cli/
│ ├── main.py # CLI entry point
│ ├── api/
│ │ ├── client.py # HTTP client (httpx)
@@ -461,7 +461,7 @@ freepik/
│ │ └── config.py # config management
│ └── utils/
│ ├── console.py # Rich console, theme, display helpers
│ ├── config.py # FreepikConfig (pydantic-settings)
│ ├── config.py # MagnificConfig (pydantic-settings)
│ ├── polling.py # Live polling with Rich
│ └── files.py # Base64 encoding, download, path utils
```
-3
View File
@@ -1,3 +0,0 @@
"""Freepik AI CLI — generate images, videos, and more."""
__version__ = "0.1.0"
+3
View File
@@ -0,0 +1,3 @@
"""Magnific AI CLI — generate images, videos, and more."""
__version__ = "0.1.0"
@@ -1,4 +1,4 @@
"""Freepik HTTP client with authentication, error handling, and download support."""
"""Magnific HTTP client with authentication, error handling, and download support."""
from __future__ import annotations
@@ -6,14 +6,14 @@ from typing import Any, Optional
import httpx
from freepik_cli import __version__
from magnific_cli import __version__
BASE_URL = "https://api.freepik.com"
BASE_URL = "https://api.magnific.ai"
DEFAULT_TIMEOUT = 60.0
class FreepikAPIError(Exception):
"""Raised when the Freepik API returns an error response."""
class MagnificAPIError(Exception):
"""Raised when the Magnific API returns an error response."""
def __init__(self, message: str, status_code: Optional[int] = None, raw: Optional[dict] = None):
super().__init__(message)
@@ -21,7 +21,7 @@ class FreepikAPIError(Exception):
self.raw = raw or {}
@classmethod
def from_response(cls, response: httpx.Response) -> "FreepikAPIError":
def from_response(cls, response: httpx.Response) -> "MagnificAPIError":
try:
body = response.json()
except Exception:
@@ -43,8 +43,8 @@ class FreepikAPIError(Exception):
message = f"{message}\n\n{details}"
hints = {
401: "Check your API key — set FREEPIK_API_KEY or use --api-key.",
403: "Your plan may not support this feature. Check your Freepik subscription.",
401: "Check your API key — set MAGNIFIC_API_KEY or use --api-key.",
403: "Your plan may not support this feature. Check your Magnific subscription.",
429: "Rate limit exceeded. Please wait before retrying.",
}
hint = hints.get(response.status_code)
@@ -54,8 +54,8 @@ class FreepikAPIError(Exception):
return cls(message, status_code=response.status_code, raw=body)
class FreepikClient:
"""Thin synchronous HTTP wrapper around the Freepik API."""
class MagnificClient:
"""Thin synchronous HTTP wrapper around the Magnific API."""
def __init__(
self,
@@ -66,10 +66,10 @@ class FreepikClient:
self._client = httpx.Client(
base_url=base_url,
headers={
"x-freepik-api-key": api_key,
"x-magnific-api-key": api_key,
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": f"freepik-cli/{__version__}",
"User-Agent": f"magnific-cli/{__version__}",
},
timeout=httpx.Timeout(timeout),
)
@@ -80,9 +80,9 @@ class FreepikClient:
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as exc:
raise FreepikAPIError.from_response(exc.response) from exc
raise MagnificAPIError.from_response(exc.response) from exc
except httpx.RequestError as exc:
raise FreepikAPIError(f"Network error: {exc}") from exc
raise MagnificAPIError(f"Network error: {exc}") from exc
def post_multipart(self, path: str, data: dict[str, Any], files: dict[str, Any]) -> dict[str, Any]:
"""POST with multipart/form-data (for file uploads)."""
@@ -92,9 +92,9 @@ class FreepikClient:
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as exc:
raise FreepikAPIError.from_response(exc.response) from exc
raise MagnificAPIError.from_response(exc.response) from exc
except httpx.RequestError as exc:
raise FreepikAPIError(f"Network error: {exc}") from exc
raise MagnificAPIError(f"Network error: {exc}") from exc
def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
try:
@@ -102,11 +102,11 @@ class FreepikClient:
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as exc:
raise FreepikAPIError.from_response(exc.response) from exc
raise MagnificAPIError.from_response(exc.response) from exc
except httpx.RequestError as exc:
raise FreepikAPIError(f"Network error: {exc}") from exc
raise MagnificAPIError(f"Network error: {exc}") from exc
def __enter__(self) -> "FreepikClient":
def __enter__(self) -> "MagnificClient":
return self
def __exit__(self, *args: Any) -> None:
@@ -4,12 +4,12 @@ from __future__ import annotations
from typing import Any, Optional, Tuple
from freepik_cli.api.client import FreepikClient
from freepik_cli.api.models import IconStyle, get_output_urls, get_status, get_task_id
from magnific_cli.api.client import MagnificClient
from magnific_cli.api.models import IconStyle, get_output_urls, get_status, get_task_id
class EditAPI:
def __init__(self, client: FreepikClient) -> None:
def __init__(self, client: MagnificClient) -> None:
self._client = client
# ------------------------------------------------------------------
@@ -4,8 +4,8 @@ from __future__ import annotations
from typing import Any, Optional, Tuple
from freepik_cli.api.client import FreepikClient
from freepik_cli.api.models import (
from magnific_cli.api.client import MagnificClient
from magnific_cli.api.models import (
IMAGE_POST_ENDPOINTS,
IMAGE_STATUS_ENDPOINTS,
ImageModel,
@@ -16,7 +16,7 @@ from freepik_cli.api.models import (
class ImageAPI:
def __init__(self, client: FreepikClient) -> None:
def __init__(self, client: MagnificClient) -> None:
self._client = client
def generate(self, model: ImageModel, payload: dict[str, Any]) -> str:
@@ -4,8 +4,8 @@ from __future__ import annotations
from typing import Any, Optional, Tuple
from freepik_cli.api.client import FreepikClient
from freepik_cli.api.models import (
from magnific_cli.api.client import MagnificClient
from magnific_cli.api.models import (
UPSCALE_POST_ENDPOINTS,
UPSCALE_STATUS_ENDPOINTS,
VIDEO_UPSCALE_POST_ENDPOINTS,
@@ -19,7 +19,7 @@ from freepik_cli.api.models import (
class UpscaleAPI:
def __init__(self, client: FreepikClient) -> None:
def __init__(self, client: MagnificClient) -> None:
self._client = client
# ------------------------------------------------------------------
@@ -4,8 +4,8 @@ from __future__ import annotations
from typing import Any, Optional, Tuple
from freepik_cli.api.client import FreepikClient
from freepik_cli.api.models import (
from magnific_cli.api.client import MagnificClient
from magnific_cli.api.models import (
VIDEO_POST_ENDPOINTS,
VIDEO_STATUS_ENDPOINTS,
VIDEO_IMAGE_FIELDS,
@@ -19,7 +19,7 @@ from freepik_cli.api.models import (
class VideoAPI:
def __init__(self, client: FreepikClient) -> None:
def __init__(self, client: MagnificClient) -> None:
self._client = client
def generate(
@@ -7,18 +7,18 @@ 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
from magnific_cli.api.client import MagnificAPIError, MagnificClient
from magnific_cli.api.images import ImageAPI
from magnific_cli.utils.config import MagnificConfig
from magnific_cli.utils.console import console, print_describe_result, print_error, print_no_wait
from magnific_cli.utils.files import image_to_base64
from magnific_cli.utils.polling import MagnificTaskError, MagnificTimeoutError, PollConfig, poll_task
def _get_api_key(api_key: Optional[str], config: FreepikConfig) -> str:
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]FREEPIK_API_KEY[/cyan] or pass [cyan]--api-key[/cyan].")
print_error("No API key found.", hint="Set [cyan]MAGNIFIC_API_KEY[/cyan] or pass [cyan]--api-key[/cyan].")
raise typer.Exit(1)
return key
@@ -41,7 +41,7 @@ def describe_image(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY"),
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY"),
] = None,
) -> None:
"""
@@ -51,20 +51,20 @@ def describe_image(
[cyan]generate-image[/cyan].
[dim]Examples:[/dim]
freepik describe-image photo.jpg
freepik describe-image scene.png --output prompt.txt
magnific describe-image photo.jpg
magnific describe-image scene.png --output prompt.txt
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
image_b64 = image_to_base64(image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(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:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -80,7 +80,7 @@ def describe_image(
config=poll_config,
console=console,
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -7,12 +7,12 @@ from typing import Annotated, Optional
import typer
from rich.prompt import Confirm
from freepik_cli.utils.config import CONFIG_FILE, FreepikConfig
from freepik_cli.utils.console import console, print_config_table, print_config_toml, print_error, print_warning
from magnific_cli.utils.config import CONFIG_FILE, MagnificConfig
from magnific_cli.utils.console import console, print_config_table, print_config_toml, print_error, print_warning
app = typer.Typer(
name="config",
help="[bold]Manage[/bold] Freepik CLI configuration.",
help="[bold]Manage[/bold] Magnific CLI configuration.",
rich_markup_mode="rich",
no_args_is_help=True,
)
@@ -26,7 +26,7 @@ def config_show(
] = False,
) -> None:
"""[bold]Show[/bold] all current configuration values."""
config = FreepikConfig.load()
config = MagnificConfig.load()
d = config.to_display_dict()
if toml:
print_config_toml(d)
@@ -40,12 +40,12 @@ def config_get(
key: Annotated[str, typer.Argument(help="Config key to retrieve")],
) -> None:
"""[bold]Get[/bold] the value of a single configuration key."""
config = FreepikConfig.load()
config = MagnificConfig.load()
d = config.to_display_dict()
if key not in d:
print_error(
f"Unknown config key: '{key}'",
hint=f"Run [cyan]freepik config show[/cyan] to see all available keys.",
hint=f"Run [cyan]magnific config show[/cyan] to see all available keys.",
)
raise typer.Exit(1)
@@ -68,14 +68,14 @@ def config_set(
[bold]Set[/bold] a configuration value.
[dim]Examples:[/dim]
freepik config set default_image_model mystic
freepik config set default_output_dir ~/images
freepik config set poll_timeout 300
magnific config set default_image_model mystic
magnific config set default_output_dir ~/images
magnific config set poll_timeout 300
[dim]Note:[/dim] The API key is never saved to disk. Use the
[cyan]FREEPIK_API_KEY[/cyan] environment variable instead.
[cyan]MAGNIFIC_API_KEY[/cyan] environment variable instead.
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
try:
config.set_value(key, value)
console.print(f"[success]✓[/success] Set [cyan]{key}[/cyan] = [bold]{value}[/bold]")
@@ -7,20 +7,20 @@ from typing import Annotated, Optional
import typer
from freepik_cli.api.client import FreepikAPIError, FreepikClient
from freepik_cli.api.edit import EditAPI
from freepik_cli.utils.config import FreepikConfig
from freepik_cli.utils.console import GenerationResult, console, print_error, print_no_wait, print_result
from freepik_cli.utils.files import auto_output_path, get_image_dimensions, image_to_base64, save_from_url
from freepik_cli.utils.polling import FreepikTaskError, FreepikTimeoutError, PollConfig, poll_task
from magnific_cli.api.client import MagnificAPIError, MagnificClient
from magnific_cli.api.edit import EditAPI
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
from magnific_cli.utils.polling import MagnificTaskError, MagnificTimeoutError, PollConfig, poll_task
_EXPAND_MODELS = ["flux-pro", "ideogram", "seedream-v4-5"]
def _get_api_key(api_key: Optional[str], config: FreepikConfig) -> str:
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]FREEPIK_API_KEY[/cyan] or pass [cyan]--api-key[/cyan].")
print_error("No API key found.", hint="Set [cyan]MAGNIFIC_API_KEY[/cyan] or pass [cyan]--api-key[/cyan].")
raise typer.Exit(1)
return key
@@ -39,14 +39,14 @@ def expand_image(
seed: Annotated[Optional[int], typer.Option("--seed")] = None,
output: Annotated[Optional[Path], typer.Option("--output", "-o")] = None,
wait: Annotated[bool, typer.Option("--wait/--no-wait")] = True,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="FREEPIK_API_KEY")] = None,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="MAGNIFIC_API_KEY")] = None,
) -> None:
"""
[bold]Expand an image[/bold] by adding new content around its edges (outpainting).
[dim]Examples:[/dim]
freepik expand-image photo.jpg --left 512 --right 512 --prompt "lush forest"
freepik expand-image banner.png --bottom 256 --model seedream-v4-5
magnific expand-image photo.jpg --left 512 --right 512 --prompt "lush forest"
magnific expand-image banner.png --bottom 256 --model seedream-v4-5
"""
if not any([left, right, top, bottom]):
print_error("At least one of --left, --right, --top, --bottom must be > 0.")
@@ -56,17 +56,17 @@ def expand_image(
print_error(f"Unknown model '{model}'.", hint=f"Choose from: {', '.join(_EXPAND_MODELS)}")
raise typer.Exit(1)
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
image_b64 = image_to_base64(image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = EditAPI(client)
with console.status("[info]Submitting image expansion…[/info]"):
try:
task_id = api.expand_submit(model, image_b64, left, right, top, bottom, prompt, seed)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -75,7 +75,7 @@ def expand_image(
return
poll_config = PollConfig(task_type="expand", max_wait=config.poll_timeout)
from freepik_cli.api.images import ImageAPI
from magnific_cli.api.images import ImageAPI
img_api = ImageAPI(client)
try:
result = poll_task(
@@ -85,11 +85,11 @@ def expand_image(
console=console,
extra_info={"Model": model, "Expand": f"L{left} R{right} T{top} B{bottom}"},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
from freepik_cli.api.models import get_output_urls
from magnific_cli.api.models import get_output_urls
urls = get_output_urls(result)
if not urls:
print_error("Expansion completed but no output URL found.")
@@ -107,26 +107,26 @@ def relight_image(
style: Annotated[Optional[str], typer.Option("--style", "-s", help="Lighting style preset")] = None,
output: Annotated[Optional[Path], typer.Option("--output", "-o")] = None,
wait: Annotated[bool, typer.Option("--wait/--no-wait")] = True,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="FREEPIK_API_KEY")] = None,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="MAGNIFIC_API_KEY")] = None,
) -> None:
"""
[bold]Relight an image[/bold] using AI-controlled lighting. [dim](Premium feature)[/dim]
[dim]Examples:[/dim]
freepik relight portrait.jpg --prompt "dramatic studio lighting"
freepik relight scene.png --prompt "warm golden hour" --output relit.jpg
magnific relight portrait.jpg --prompt "dramatic studio lighting"
magnific relight scene.png --prompt "warm golden hour" --output relit.jpg
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
image_b64 = image_to_base64(image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = EditAPI(client)
with console.status("[info]Submitting image relight…[/info]"):
try:
task_id = api.relight_submit(image_b64, prompt, style)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -142,7 +142,7 @@ def relight_image(
config=poll_config,
console=console,
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -163,28 +163,28 @@ def style_transfer(
strength: Annotated[Optional[float], typer.Option("--strength", help="Style strength 0.01.0", min=0.0, max=1.0)] = None,
output: Annotated[Optional[Path], typer.Option("--output", "-o")] = None,
wait: Annotated[bool, typer.Option("--wait/--no-wait")] = True,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="FREEPIK_API_KEY")] = None,
api_key: Annotated[Optional[str], typer.Option("--api-key", envvar="MAGNIFIC_API_KEY")] = None,
) -> None:
"""
[bold]Apply an artistic style[/bold] from one image onto another. [dim](Premium feature)[/dim]
[dim]Examples:[/dim]
freepik style-transfer photo.jpg painting.jpg --strength 0.8
freepik style-transfer portrait.png van_gogh.jpg --output styled.jpg
magnific style-transfer photo.jpg painting.jpg --strength 0.8
magnific style-transfer portrait.png van_gogh.jpg --output styled.jpg
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
content_b64 = image_to_base64(content)
style_b64 = image_to_base64(style_image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = EditAPI(client)
with console.status("[info]Submitting style transfer…[/info]"):
try:
task_id = api.style_transfer_submit(content_b64, style_b64, strength)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -200,7 +200,7 @@ def style_transfer(
config=poll_config,
console=console,
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -7,23 +7,23 @@ from typing import Annotated, Optional
import typer
from freepik_cli.api.client import FreepikAPIError, FreepikClient
from freepik_cli.api.edit import EditAPI
from freepik_cli.api.images import ImageAPI
from freepik_cli.api.models import IconStyle, ImageModel, VideoModel, normalize_aspect_ratio
from freepik_cli.api.videos import VideoAPI
from freepik_cli.utils.config import FreepikConfig
from freepik_cli.utils.console import GenerationResult, console, print_error, print_no_wait, print_result
from freepik_cli.utils.files import auto_output_path, get_image_dimensions, image_to_base64, save_from_url
from freepik_cli.utils.polling import FreepikTaskError, FreepikTimeoutError, PollConfig, poll_task
from magnific_cli.api.client import MagnificAPIError, MagnificClient
from magnific_cli.api.edit import EditAPI
from magnific_cli.api.images import ImageAPI
from magnific_cli.api.models import IconStyle, ImageModel, VideoModel, normalize_aspect_ratio
from magnific_cli.api.videos import VideoAPI
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
from magnific_cli.utils.polling import MagnificTaskError, MagnificTimeoutError, PollConfig, poll_task
def _get_api_key(api_key: Optional[str], config: FreepikConfig) -> str:
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 the [cyan]FREEPIK_API_KEY[/cyan] environment variable, "
hint="Set the [cyan]MAGNIFIC_API_KEY[/cyan] environment variable, "
"or pass [cyan]--api-key YOUR_KEY[/cyan].",
)
raise typer.Exit(1)
@@ -69,18 +69,18 @@ def generate_image(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY", help="Freepik API key"),
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY", help="Magnific API key"),
] = None,
) -> None:
"""
[bold]Generate an image[/bold] using Freepik AI models.
[bold]Generate an image[/bold] using Magnific AI models.
[dim]Examples:[/dim]
freepik generate-image "a cat on the moon" --model flux-2-pro
freepik generate-image "cyberpunk city at night" --model mystic --aspect-ratio 16:9
freepik generate-image "make the sky orange" --model flux-kontext-pro --input-image photo.jpg
magnific generate-image "a cat on the moon" --model flux-2-pro
magnific generate-image "cyberpunk city at night" --model mystic --aspect-ratio 16:9
magnific generate-image "make the sky orange" --model flux-kontext-pro --input-image photo.jpg
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
# Build request payload
@@ -94,13 +94,13 @@ def generate_image(
if input_image:
payload["image"] = image_to_base64(input_image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = ImageAPI(client)
with console.status(f"[info]Submitting {model.value} generation…[/info]"):
try:
task_id = api.generate(model, payload)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -117,13 +117,13 @@ def generate_image(
console=console,
extra_info={"Model": f"[magenta]{model.value}[/magenta]"},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
urls = api.get_output_urls(result)
if not urls:
print_error("Generation completed but no output URLs found.", hint="Check the Freepik dashboard.")
print_error("Generation completed but no output URLs found.", hint="Check the Magnific dashboard.")
raise typer.Exit(1)
out = output or auto_output_path("image", model.value, "jpg", config.default_output_dir)
@@ -185,23 +185,23 @@ def generate_video(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY", help="Freepik API key"),
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY", help="Magnific API key"),
] = None,
) -> None:
"""
[bold]Generate a video[/bold] from a source image using AI.
[dim]Examples:[/dim]
freepik generate-video photo.jpg --prompt "gentle ocean waves" --model kling-o1-pro
freepik generate-video portrait.png --model kling-elements-pro --aspect-ratio 9:16
freepik generate-video photo.jpg --model minimax-hailuo --aspect-ratio 16:9
magnific generate-video photo.jpg --prompt "gentle ocean waves" --model kling-o1-pro
magnific generate-video portrait.png --model kling-elements-pro --aspect-ratio 9:16
magnific generate-video photo.jpg --model minimax-hailuo --aspect-ratio 16:9
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
image_b64 = image_to_base64(image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = VideoAPI(client)
with console.status(f"[info]Submitting {model.value} video generation…[/info]"):
@@ -214,7 +214,7 @@ def generate_video(
aspect_ratio=aspect_ratio,
seed=seed,
)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -239,7 +239,7 @@ def generate_video(
"Ratio": aspect_ratio,
},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -294,26 +294,26 @@ def generate_icon(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY"),
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY"),
] = None,
) -> None:
"""
[bold]Generate an icon[/bold] from a text prompt.
[dim]Examples:[/dim]
freepik generate-icon "shopping cart" --style solid --format svg
freepik generate-icon "rocket ship" --style color --format png
magnific generate-icon "shopping cart" --style solid --format svg
magnific generate-icon "rocket ship" --style color --format png
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = EditAPI(client)
with console.status("[info]Submitting icon generation…[/info]"):
try:
task_id = api.generate_icon(prompt, style, steps, guidance, seed)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -330,7 +330,7 @@ def generate_icon(
console=console,
extra_info={"Style": style.value, "Format": fmt},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -338,7 +338,7 @@ def generate_icon(
with console.status(f"[info]Rendering icon as {fmt.upper()}…[/info]"):
try:
url = api.render_icon(task_id, fmt)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -7,21 +7,21 @@ from typing import Annotated, Optional
import typer
from freepik_cli.api.client import FreepikAPIError, FreepikClient
from freepik_cli.api.models import UpscaleMode, VideoUpscaleMode
from freepik_cli.api.upscale import UpscaleAPI
from freepik_cli.utils.config import FreepikConfig
from freepik_cli.utils.console import GenerationResult, console, print_error, print_no_wait, print_result
from freepik_cli.utils.files import auto_output_path, get_image_dimensions, image_to_base64, save_from_url, video_to_base64
from freepik_cli.utils.polling import FreepikTaskError, FreepikTimeoutError, PollConfig, poll_task
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: FreepikConfig) -> str:
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]FREEPIK_API_KEY[/cyan] or pass [cyan]--api-key YOUR_KEY[/cyan].",
hint="Set [cyan]MAGNIFIC_API_KEY[/cyan] or pass [cyan]--api-key YOUR_KEY[/cyan].",
)
raise typer.Exit(1)
return key
@@ -76,17 +76,17 @@ def upscale_image(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY", help="Freepik API key"),
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]
freepik upscale-image photo.jpg --mode precision-v2 --scale 4x
freepik upscale-image portrait.png --mode creative --creativity 7 --prompt "sharp cinematic"
magnific upscale-image photo.jpg --mode precision-v2 --scale 4x
magnific upscale-image portrait.png --mode creative --creativity 7 --prompt "sharp cinematic"
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
if mode != UpscaleMode.CREATIVE and (creativity is not None or prompt):
@@ -98,7 +98,7 @@ def upscale_image(
image_b64 = image_to_base64(image)
with FreepikClient(key, base_url=config.base_url) as client:
with MagnificClient(key, base_url=config.base_url) as client:
api = UpscaleAPI(client)
with console.status(f"[info]Submitting {mode.value} upscale ({scale})…[/info]"):
@@ -111,7 +111,7 @@ def upscale_image(
prompt=prompt,
seed=seed,
)
except FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -128,7 +128,7 @@ def upscale_image(
console=console,
extra_info={"Mode": mode.value, "Scale": scale},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -179,28 +179,28 @@ def upscale_video(
] = True,
api_key: Annotated[
Optional[str],
typer.Option("--api-key", envvar="FREEPIK_API_KEY"),
typer.Option("--api-key", envvar="MAGNIFIC_API_KEY"),
] = None,
) -> None:
"""
[bold]Upscale a video[/bold] to higher resolution using AI.
[dim]Examples:[/dim]
freepik upscale-video clip.mp4 --mode standard
freepik upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
magnific upscale-video clip.mp4 --mode standard
magnific upscale-video clip.mp4 --mode turbo --output clip_4k.mp4
"""
config = FreepikConfig.load()
config = MagnificConfig.load()
key = _get_api_key(api_key, config)
video_b64 = video_to_base64(video)
with FreepikClient(key, base_url=config.base_url) as client:
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 FreepikAPIError as exc:
except MagnificAPIError as exc:
print_error(str(exc))
raise typer.Exit(1)
@@ -221,7 +221,7 @@ def upscale_video(
console=console,
extra_info={"Mode": mode.value},
)
except (FreepikTaskError, FreepikTimeoutError) as exc:
except (MagnificTaskError, MagnificTimeoutError) as exc:
print_error(str(exc))
raise typer.Exit(1)
+14 -14
View File
@@ -6,22 +6,22 @@ from typing import Annotated, Optional
import typer
from freepik_cli import __version__
from freepik_cli.commands import config as config_cmd
from freepik_cli.commands.analyze import describe_image
from freepik_cli.commands.edit import expand_image, relight_image, style_transfer
from freepik_cli.commands.generate import generate_icon, generate_image, generate_video
from freepik_cli.commands.upscale import upscale_image, upscale_video
from freepik_cli.utils.console import console, print_banner
from freepik_cli.utils.config import FreepikConfig
from magnific_cli import __version__
from magnific_cli.commands import config as config_cmd
from magnific_cli.commands.analyze import describe_image
from magnific_cli.commands.edit import expand_image, relight_image, style_transfer
from magnific_cli.commands.generate import generate_icon, generate_image, generate_video
from magnific_cli.commands.upscale import upscale_image, upscale_video
from magnific_cli.utils.console import console, print_banner
from magnific_cli.utils.config import MagnificConfig
app = typer.Typer(
name="freepik",
name="magnific",
help=(
"[bold magenta]Freepik AI[/bold magenta] — generate images, videos, and more "
"[bold magenta]Magnific AI[/bold magenta] — generate images, videos, and more "
"from the command line.\n\n"
"[dim]Set your API key:[/dim] [cyan]export FREEPIK_API_KEY=your_key[/cyan]\n"
"[dim]Get an API key:[/dim] https://www.freepik.com/api"
"[dim]Set your API key:[/dim] [cyan]export MAGNIFIC_API_KEY=your_key[/cyan]\n"
"[dim]Get an API key:[/dim] https://magnific.ai"
),
rich_markup_mode="rich",
no_args_is_help=True,
@@ -106,9 +106,9 @@ def main(
] = False,
) -> None:
"""
[bold magenta]Freepik AI[/bold magenta] generate images, videos, icons, and more.
[bold magenta]Magnific AI[/bold magenta] generate images, videos, icons, and more.
"""
cfg = FreepikConfig.load()
cfg = MagnificConfig.load()
if version:
print_banner()
@@ -10,28 +10,28 @@ from platformdirs import user_config_dir
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
CONFIG_DIR = Path(user_config_dir("freepik-cli"))
CONFIG_DIR = Path(user_config_dir("magnific-cli"))
CONFIG_FILE = CONFIG_DIR / "config.toml"
class FreepikConfig(BaseSettings):
class MagnificConfig(BaseSettings):
"""
Configuration with priority (highest to lowest):
1. CLI --api-key flag (handled in commands directly)
2. FREEPIK_* environment variables
3. ~/.config/freepik-cli/config.toml
2. MAGNIFIC_* environment variables
3. ~/.config/magnific-cli/config.toml
4. Defaults below
"""
model_config = SettingsConfigDict(
env_prefix="FREEPIK_",
env_prefix="MAGNIFIC_",
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
api_key: Optional[str] = None
base_url: str = "https://api.freepik.com"
base_url: str = "https://api.magnific.ai"
default_output_dir: str = "."
default_image_model: str = "flux-2-pro"
default_video_model: str = "kling-o1-pro"
@@ -46,7 +46,7 @@ class FreepikConfig(BaseSettings):
return v.strip() if isinstance(v, str) else v
@classmethod
def load(cls) -> "FreepikConfig":
def load(cls) -> "MagnificConfig":
"""Load from config file, then overlay environment variables."""
file_data: dict = {}
if CONFIG_FILE.exists():
@@ -83,7 +83,7 @@ class FreepikConfig(BaseSettings):
if key not in allowed:
raise ValueError(
f"Key '{key}' is not configurable via this command. "
f"Use the FREEPIK_API_KEY environment variable to set the API key."
f"Use the MAGNIFIC_API_KEY environment variable to set the API key."
)
current = self.model_dump()
if key in ("poll_timeout", "poll_max_interval"):
@@ -92,5 +92,5 @@ class FreepikConfig(BaseSettings):
current[key] = value.lower() in ("true", "1", "yes")
else:
current[key] = value
updated = FreepikConfig(**{k: v for k, v in current.items() if v is not None})
updated = MagnificConfig(**{k: v for k, v in current.items() if v is not None})
updated.save()
@@ -17,7 +17,7 @@ from rich.table import Table
from rich.text import Text
from rich.theme import Theme
FREEPIK_THEME = Theme(
MAGNIFIC_THEME = Theme(
{
"info": "bold cyan",
"success": "bold green",
@@ -32,17 +32,17 @@ FREEPIK_THEME = Theme(
}
)
console = Console(theme=FREEPIK_THEME, highlight=True)
err_console = Console(stderr=True, theme=FREEPIK_THEME)
console = Console(theme=MAGNIFIC_THEME, highlight=True)
err_console = Console(stderr=True, theme=MAGNIFIC_THEME)
BANNER = """\
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[dim] AI Media Generation CLI v0.1.0[/dim]"""
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[bold magenta] [/bold magenta]
[dim] AI Media Generation CLI v0.1.0[/dim]"""
def print_banner() -> None:
@@ -128,7 +128,7 @@ def print_no_wait(task_id: str, task_type: str, model: str) -> None:
f"[dim.label]Type:[/dim.label] {task_type}\n"
f"[dim.label]Model:[/dim.label] [model]{model}[/model]\n\n"
f"[dim]The task is processing asynchronously. Results will be available\n"
f"on the Freepik dashboard when complete.[/dim]"
f"on the Magnific dashboard when complete.[/dim]"
),
title="⏳ Task Queued",
border_style="cyan",
@@ -165,7 +165,7 @@ def print_warning(message: str) -> None:
def print_config_table(config_dict: dict, masked_keys: set[str] | None = None) -> None:
masked_keys = masked_keys or {"api_key"}
table = Table(
title="[brand]Freepik CLI Configuration[/brand]",
title="[brand]Magnific CLI Configuration[/brand]",
show_header=True,
header_style="bold magenta",
border_style="magenta",
@@ -206,7 +206,7 @@ def print_config_toml(config_dict: dict, masked_keys: set[str] | None = None) ->
console.print(
Panel(
Syntax(toml_str, "toml", theme="monokai", background_color="default"),
title="[brand]~/.config/freepik-cli/config.toml[/brand]",
title="[brand]~/.config/magnific-cli/config.toml[/brand]",
border_style="magenta",
padding=(1, 2),
)
@@ -62,7 +62,7 @@ def auto_output_path(task_type: str, model: str, ext: str = "jpg", output_dir: s
"""Generate a timestamped output filename."""
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
safe_model = model.replace("/", "-").replace(" ", "-")
filename = f"freepik_{task_type}_{safe_model}_{ts}.{ext}"
filename = f"magnific_{task_type}_{safe_model}_{ts}.{ext}"
return Path(output_dir) / filename
@@ -46,11 +46,11 @@ TASK_TYPE_LABELS: dict[str, str] = {
}
class FreepikTaskError(Exception):
class MagnificTaskError(Exception):
pass
class FreepikTimeoutError(Exception):
class MagnificTimeoutError(Exception):
pass
@@ -104,7 +104,7 @@ def _render_panel(
return Panel(
grid,
title="[bold magenta]~ Freepik AI ~[/bold magenta]",
title="[bold magenta]~ Magnific AI ~[/bold magenta]",
border_style="magenta",
padding=(1, 2),
width=52,
@@ -149,7 +149,7 @@ def poll_task(
if elapsed > config.max_wait:
live.stop()
raise FreepikTimeoutError(
raise MagnificTimeoutError(
f"Task {task_id} timed out after {config.max_wait:.0f}s"
)
@@ -178,7 +178,7 @@ def poll_task(
or data.get("message")
or f"Task ended with status {upper}"
)
raise FreepikTaskError(f"{upper}: {error_msg}")
raise MagnificTaskError(f"{upper}: {error_msg}")
time.sleep(interval)
interval = min(interval * config.backoff_factor, config.max_interval)
+4 -4
View File
@@ -3,9 +3,9 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "freepik-cli"
name = "magnific-cli"
version = "0.1.0"
description = "A beautiful CLI for the Freepik AI API — generate images, videos, and more"
description = "A beautiful CLI for the Magnific AI API — generate images, videos, and more"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
@@ -30,10 +30,10 @@ dev = [
]
[project.scripts]
freepik = "freepik_cli.main:app"
magnific = "magnific_cli.main:app"
[tool.hatch.build.targets.wheel]
packages = ["freepik_cli"]
packages = ["magnific_cli"]
[tool.ruff]
line-length = 100