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