Skill: yuan-huicheng/ali-minimax-toolkit (version 1.0.0)

--- name: ali-minimax-toolkit description: MiniMax multimodal generation via API. Use when user wants voice, music, image, image-to-image, or video generation with MiniMax. Supports TTS, music, image (t2i + i2i), and video (t2v, i2v, sef, ref). Cross-platform Python scripts. --- # MiniMax Multimodal Toolkit Generate voice, music, image, and video content via MiniMax APIs. **Pure Python** — works on Windows, Mac, and Linux without any third-party dependencies. ## Prerequisites - `MINIMAX_API_KEY` environment variable (starts with `sk-`) - `MINIMAX_API_HOST` environment variable (optional, default: `https://api.minimaxi.com`) - Python 3.6+ - For video duration detection: `ffprobe` (optional) ## Quick Start ```bash # Load the Python module import sys; sys.path.insert(0, "{skillDir}/scripts"); import minimax_api ``` Or use CLI directly: ```bash python "{skillDir}/scripts/minimax_api.py" tts "Hello world" -o minimax-output/hello.mp3 python "{skillDir}/scripts/minimax_api.py" image "A cute cat" -o minimax-output/cat.png ``` ## Output Convention All generated files MUST be saved to `minimax-output/` under the agent's working directory. ## TTS (Text-to-Speech) Endpoint: `POST /v1/t2a_v2` — returns hex audio, decoded and saved as file. **Models:** `speech-2.8-hd` (recommended, best quality), `speech-2.8-turbo` (faster), `speech-02-hd`, `speech-02-turbo` ```python # Basic TTS minimax_api.generate_tts("Hello world", output="minimax-output/hello.mp3") # Chinese with specific voice minimax_api.generate_tts("红叶最多情，一舞寄相思", voice_id="female-shaonv", output="minimax-output/greeting.mp3") # With emotion minimax_api.generate_tts("I'm so happy today!", voice_id="male-qn-qingse", emotion="happy", output="minimax-output/happy.mp3") ``` **Common voice IDs:** `female-shaonv`, `male-qn-qingse`, `male-qn-jingying`, `presenter_male`, `presenter_female` **Emotions:** `happy`, `sad`, `angry`, `fearful`, `disgusted`, `surprised`, `calm`, `fluent`, `whisper` (empty = auto) ## Music Generation Endpoint: `POST /v1/music_generation` — lyrics required, returns audio URL. **Takes 30-300 seconds.** ```python # Instrumental (BGM) minimax_api.generate_music("soft piano, ambient, peaceful", instrumental=True, output="minimax-output/bgm.mp3") # Song with lyrics minimax_api.generate_music( "indie folk, melancholic", lyrics="[verse]\nWalking alone\n[chorus]\nFeeling free", output="minimax-output/song.mp3" ) ``` ## Image Generation (Text-to-Image) Endpoint: `POST /v1/image_generation` — returns image URLs (immediate). ```python # Basic minimax_api.generate_image("A cute cat on a windowsill, photorealistic", output="minimax-output/cat.png") # With aspect ratio minimax_api.generate_image("Mountain landscape, golden hour", aspect_ratio="16:9", output="minimax-output/landscape.png") # Multiple images minimax_api.generate_image("Abstract geometric art, vibrant", count=3, output="minimax-output/art.png") # With prompt optimizer minimax_api.generate_image("A man on Venice Beach, 90s documentary", prompt_optimizer=True, output="minimax-output/beach.png") ``` **Aspect ratios:** `1:1` (default), `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9` ## Image-to-Image Generation Endpoint: `POST /v1/image_generation` with `image_file` — generate new images from a reference. ```python # From local file minimax_api.image_to_image("A girl in a library", "minimax-output/face.jpg", output="minimax-output/library.png") # From URL minimax_api.image_to_image("Oil painting style", "https://example.com/photo.jpg", output="minimax-output/painting.png") ``` ## Video Generation Endpoint: `POST /v1/video_generation` (async) + `GET /v1/query/video_generation` — polling required. ```python # Text-to-video minimax_api.generate_video( "A golden retriever puppy runs toward camera, tracking shot, golden hour", output="minimax-output/puppy.mp4" ) # Image-to-video (prompt focuses on MOTION only) minimax_api.generate_video( "Petals sway in breeze, soft light shifts", mode="i2v", first_frame="minimax-output/flower.png", output="minimax-output/flower_video.mp4" ) # Subject reference (face consistency) minimax_api.generate_video( "A woman walks through a garden, tracking shot", mode="ref", subject_image="minimax-output/face.jpg", output="minimax-output/garden.mp4" ) ``` **Models:** `MiniMax-Hailuo-2.3` (default), `MiniMax-Hailuo-2.3-Fast` (i2v), `MiniMax-Hailuo-02` (1080P, 10s) **Modes:** `t2v`, `i2v`, `sef` (start-end frame), `ref` (subject reference) ### Video Prompt Tips Main subject + Scene + Movement + Camera motion + Aesthetic. For i2v: describe motion only, don't repeat what's in the image. ## Generate & Send to Feishu Use `generate_and_send.py` to generate content and prepare for Feishu delivery via the `feishu-media` skill: ```bash # Generate TTS and send python "{skillDir}/scripts/generate_and_send.py" tts "Hello" --voice female-shaonv --feishu-chat <chat_id> # Generate image and send python "{skillDir}/scripts/generate_and_send.py" image "A sunset" --ratio 16:9 --feishu-chat <chat_id> # Set FEISHU_CHAT_ID env var to avoid passing --feishu-chat every time export FEISHU_CHAT_ID=oc_xxxxx ``` After generation, the script outputs file paths and feishu-media send instructions. Use the `feishu-media` skill to actually deliver the content. ## Legacy PowerShell Script The original `scripts/minimax-api.ps1` is preserved for backward compatibility but is **deprecated**. Use the Python scripts instead. ## Error Handling | Error Code | Meaning | Solution | |-----------|---------|----------| | 2061 | Plan doesn't support model | Try `speech-02-turbo` for TTS | | 1008 | Insufficient balance | Top up MiniMax account | | 2013 | Invalid params | Check required fields | ## References See `references/` folder for detailed API docs, voice catalogs, and prompt guides.

path: references/image-api.md
content: # MiniMax Image Generation API (image-01)

Source: https://platform.minimaxi.com/docs/api-reference/image-generation-t2i and https://platform.minimaxi.com/docs/api-reference/image-generation-i2i

## Endpoint

`POST https://api.minimaxi.com/v1/image_generation`

## Auth

`Authorization: Bearer <MINIMAX_API_KEY>`

## Request (JSON)

Required:
- `model`: string — `image-01`
- `prompt`: string (max 1500 chars) — text description of the desired image

Optional:
- `aspect_ratio`: string — image aspect ratio, default `1:1`. Options:
  - `1:1` (1024×1024)
  - `16:9` (1280×720)
  - `4:3` (1152×864)
  - `3:2` (1248×832)
  - `2:3` (832×1248)
  - `3:4` (864×1152)
  - `9:16` (720×1280)
  - `21:9` (1344×576)
- `width`: integer — custom width in pixels. Range [512, 2048], must be multiple of 8. Overridden by `aspect_ratio` if both set.
- `height`: integer — custom height in pixels. Same rules as `width`. Both `width` and `height` must be set together.
- `response_format`: string — `url` (default, valid 24h) or `base64`
- `n`: integer (1–9, default 1) — number of images to generate
- `seed`: integer — random seed for reproducibility
- `prompt_optimizer`: boolean (default `false`) — enable automatic prompt optimization
- `aigc_watermark`: boolean (default `false`) — add AIGC watermark

### Subject Reference (image-to-image)

- `subject_reference`: array — character reference for image-to-image generation
  - `type`: string — currently only `character` (portrait)
  - `image_file`: string — reference image as public URL or Base64 Data URL (`data:image/jpeg;base64,...`). For best results, use a single person front-facing photo. Formats: JPG, JPEG, PNG. Max size: 10MB.

## Example — Text-to-Image

```json
{
  "model": "image-01",
  "prompt": "A man in a white t-shirt, full-body, standing front view, outdoors, with the Venice Beach sign in the background, Los Angeles. Fashion photography in 90s documentary style, film grain, photorealistic.",
  "aspect_ratio": "16:9",
  "response_format": "url",
  "n": 3,
  "prompt_optimizer": true
}
```

## Example — Image-to-Image (Character Reference)

```json
{
  "model": "image-01",
  "prompt": "A girl looking into the distance from a library window",
  "aspect_ratio": "16:9",
  "subject_reference": [
    {
      "type": "character",
      "image_file": "https://example.com/face.jpg"
    }
  ],
  "n": 2
}
```

## Response

```json
{
  "id": "03ff3cd0820949eb8a410056b5f21d38",
  "data": {
    "image_urls": ["https://...", "https://...", "https://..."],
    "image_base64": null
  },
  "metadata": {
    "success_count": 3,
    "failed_count": 0
  },
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}
```

- `data.image_urls`: array of image URLs (when `response_format` is `url`, valid 24h)
- `data.image_base64`: array of Base64 strings (when `response_format` is `base64`)
- `metadata.success_count`: number of successfully generated images
- `metadata.failed_count`: number of images blocked by content safety

## Status Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1002 | Rate limited, retry later |
| 1004 | Auth failed, check API key |
| 1008 | Insufficient balance |
| 1026 | Prompt contains sensitive content |
| 2013 | Invalid parameters |
| 2049 | Invalid API key |

## Notes

- The API is synchronous — images are returned directly in the response (no polling needed).
- URL format image links expire after 24 hours.
- For image-to-image: upload a single front-facing portrait for best character reference results.
- `width`/`height` are overridden by `aspect_ratio` if both provided.


path: references/music-api.md
content: # MiniMax Music Generation API (music-2.5)

Source: https://platform.minimaxi.com/docs/api-reference/music-generation

## Endpoint

`POST https://api.minimaxi.com/v1/music_generation`

## Auth

`Authorization: Bearer <MINIMAX_API_KEY>`

## Request (JSON)

Required:
- `model`: string — `music-2.5`
- `lyrics`: string (1–3500 chars) — required. Use `\n` for line breaks. Structure tags: `[Verse]`, `[Chorus]`, `[Bridge]`, `[Intro]`, `[Outro]`, etc.

Optional:
- `prompt`: string (0–2000 chars) — style description, optional but recommended.
- `lyrics_optimizer`: boolean — auto-generate lyrics from prompt when lyrics is empty.
- `stream`: boolean (default `false`)
- `output_format`: `hex` (default) or `url`. URL valid for 24 hours.
- `aigc_watermark`: boolean — top-level field, non-streaming only.
- `audio_setting`:
  - `sample_rate`: 16000, 24000, 32000, 44100
  - `bitrate`: 32000, 64000, 128000, 256000
  - `format`: mp3, wav, pcm

## Example

```json
{
  "model": "music-2.5",
  "prompt": "indie folk, melancholic, introspective",
  "lyrics": "[verse]\n...\n[chorus]\n...",
  "aigc_watermark": false,
  "audio_setting": {
    "sample_rate": 44100,
    "bitrate": 256000,
    "format": "mp3"
  }
}
```

## Response

- `data.audio`: hex string or URL depending on `output_format`
- `data.status`: 1 (generating), 2 (complete)
- `extra_info`: duration, sample_rate, channels, bitrate, size
- `base_resp.status_code`: 0 on success

## Notes

- `music-2.5` does not support `is_instrumental`. For instrumental music, use lyrics `[intro] [outro]` and add `pure music, no lyrics` to the prompt.
- `prompt` is optional but recommended for better style control.
- `stream=true` only supports `hex` output.


path: references/tts-guide.md
content: # TTS Guide

## Setup

```bash
cd skills/MiniMaxStudio
pip install -r requirements.txt
brew install ffmpeg   # macOS (or: sudo apt install ffmpeg)
export MINIMAX_API_KEY="your-api-key"   # sk-api-xxx or sk-cp-xxx
python scripts/check_environment.py
```

## Quick Test

```bash
python scripts/tts/generate_voice.py tts "Hello, this is a test." -o test.mp3
```

## Voice Management

List available voices:

```bash
python scripts/tts/generate_voice.py list-voices
```

### Voice Cloning

Create a custom voice from an audio sample:

```bash
python scripts/tts/generate_voice.py clone audio.mp3 --voice-id my-custom-voice

# With preview
python scripts/tts/generate_voice.py clone audio.mp3 --voice-id my-voice --preview "Test text" --preview-output preview.mp3
```

Requirements: 10s–5min duration, ≤20MB, mp3/wav/m4a format.

### Voice Design

Design a voice from a text description:

```bash
python scripts/tts/generate_voice.py design "A warm, gentle female voice" --voice-id designed-voice
```

Custom voices expire after 7 days if not used with TTS.

## Audio Processing

### Merge

```bash
python scripts/tts/generate_voice.py merge file1.mp3 file2.mp3 -o combined.mp3
python scripts/tts/generate_voice.py merge a.mp3 b.mp3 -o merged.mp3 --crossfade 300
```

### Convert

```bash
python scripts/tts/generate_voice.py convert input.wav -o output.mp3
python scripts/tts/generate_voice.py convert input.wav -o output.mp3 --format mp3 --bitrate 192k --sample-rate 32000
```

FFmpeg required. Supported formats: mp3, wav, flac, ogg, m4a, aac, wma, opus, pcm.

## Segment-Based TTS

For multi-voice, multi-emotion workflows using a `segments.json` file:

```bash
# Validate
python scripts/tts/generate_voice.py validate segments.json --verbose

# Generate
python scripts/tts/generate_voice.py generate segments.json -o output.mp3 --crossfade 200
```

### segments.json Format

```json
[
  { "text": "Hello!", "voice_id": "female-shaonv", "emotion": "" },
  { "text": "How are you?", "voice_id": "male-qn-qingse", "emotion": "happy" }
]
```

- `text` (required): Text to synthesize
- `voice_id` (required): Voice ID
- `emotion` (optional): For speech-2.8 models, leave empty for auto-matching. Valid values: happy, sad, angry, fearful, disgusted, surprised, calm, fluent, whisper

## Troubleshooting

| Error | Solution |
|-------|----------|
| `MINIMAX_API_KEY is required` | `export MINIMAX_API_KEY="key"` |
| `FFmpeg not installed` | `brew install ffmpeg` |
| `Voice not found` | `python scripts/tts/generate_voice.py list-voices` |
| `401 Unauthorized` | Check API key validity |
| `429 Too Many Requests` | Add delays between requests |

## API Details

- **Endpoint**: `POST /v1/t2a_v2`
- **Base URL**: `https://api.minimaxi.com`
- **Auth**: `Authorization: Bearer {MINIMAX_API_KEY}`
- **Models**: speech-2.8-hd (recommended), speech-2.8-turbo, speech-2.6-hd, speech-2.6-turbo, speech-02-hd, speech-02-turbo, speech-01-hd, speech-01-turbo
- **Text limit**: 10,000 characters per request
- **Pause marker**: `<#x#>` where x is seconds (0.01–99.99)
- **Interjection tags** (speech-2.8 only): `(laughs)`, `(chuckle)`, `(coughs)`, `(sighs)`, `(breath)`, etc.


path: references/tts-voice-catalog.md
content: # TTS Voice Catalog

## Contents

- [Voice Selection Guide](#voice-selection-guide)
- [System Voices by Language](#system-voices-by-language)
- [Voice Parameters](#voice-parameters)
- [Custom Voices](#custom-voices)

---

## Voice Selection Guide

### Decision Flow

```
Content type?
├── Narration / Audiobook  → audiobook_female_1, audiobook_male_1
├── News / Announcement    → Chinese (Mandarin)_News_Anchor, Chinese (Mandarin)_Male_Announcer
├── Documentary            → doc_commentary
└── Other                  → Select by: Gender → Age → Language → Personality
```

### Recommended Professional Voices

| Scenario | Recommended | Characteristics |
|----------|-------------|-----------------|
| Narration / Audiobook | `audiobook_female_1`, `audiobook_male_1` | Clear articulation, good pacing, sustained performance |
| News / Announcement | `Chinese (Mandarin)_News_Anchor`, `Chinese (Mandarin)_Male_Announcer` | Authoritative, professional pacing |
| Documentary | `doc_commentary` | Professional, clear, consistent |

### Selection Priority

1. **Gender** (mandatory match) — male voices for male characters, female for female
2. **Age** — Children / Youth / Adult / Elderly
3. **Language** (must match content language)
4. **Personality/tone** — choose best fit from matching candidates

---

## System Voices by Language

Gender: M = Male, F = Female, N = Neutral/Character
Age: C = Child, Y = Youth, A = Adult, E = Elder

### Chinese Mandarin (普通话)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `male-qn-qingse` | 青涩青年 | M | Y | Youthful, inexperienced | Campus, coming-of-age |
| `male-qn-badao` | 霸道青年 | M | Y | Arrogant, dominant | Drama, romance |
| `male-qn-daxuesheng` | 青年大学生 | M | Y | University student | Campus, educational |
| `male-qn-jingying` | 精英青年 | M | A | Elite, ambitious | Business, professional |
| `female-shaonv` | 少女 | F | Y | Young maiden | Romance, youth |
| `female-yujie` | 御姐 | F | A | Mature, elegant | Romance, professional |
| `female-chengshu` | 成熟女性 | F | A | Mature, reliable | Sophisticated, news |
| `female-tianmei` | 甜美女性 | F | A | Sweet, pleasant | Soft, gentle |
| `clever_boy` | 聪明男童 | M | C | Smart, witty | Children's, educational |
| `cute_boy` | 可爱男童 | M | C | Adorable | Kids, animations |
| `lovely_girl` | 萌萌女童 | F | C | Cute, sweet | Children's stories |
| `cartoon_pig` | 卡通猪小琪 | N | C | Cartoon character | Animations, comedy |
| `bingjiao_didi` | 病娇弟弟 | M | Y | Tsundere brother | Romance, character |
| `junlang_nanyou` | 俊朗男友 | M | Y | Handsome boyfriend | Romance, dating |
| `chunzhen_xuedi` | 纯真学弟 | M | Y | Innocent junior | Campus, youth |
| `lengdan_xiongzhang` | 冷淡学长 | M | Y | Cool senior | Campus, romance |
| `badao_shaoye` | 霸道少爷 | M | A | Arrogant young master | Drama, character |
| `tianxin_xiaoling` | 甜心小玲 | F | Y | Sweet Xiao Ling | Character, animations |
| `qiaopi_mengmei` | 俏皮萌妹 | F | Y | Playful cute girl | Comedy, light-hearted |
| `wumei_yujie` | 妩媚御姐 | F | A | Charming mature woman | Romance, mature |
| `diadia_xuemei` | 嗲嗲学妹 | F | Y | Flirty junior girl | Romance, dating |
| `danya_xuejie` | 淡雅学姐 | F | Y | Elegant senior girl | Campus, romance |
| `Arrogant_Miss` | 嚣张小姐 | F | A | Arrogant young lady | Drama, character |
| `Robot_Armor` | 机械战甲 | N | A | Robotic armor | Sci-fi, games |
| `audiobook_male_1` | 有声书男1 | M | A | Warm, engaging narrator | Audiobooks, stories |
| `audiobook_female_1` | 有声书女1 | F | A | Gentle, expressive narrator | Audiobooks, stories |
| `doc_commentary` | 纪录片解说 | M | A | Professional narrator | Documentary |
| `Chinese (Mandarin)_News_Anchor` | 新闻女声 | F | A | News anchor | News, broadcasts |
| `Chinese (Mandarin)_Male_Announcer` | 播报男声 | M | A | Male announcer | Announcements |
| `Chinese (Mandarin)_Radio_Host` | 电台男主播 | M | A | Radio host | Podcasts, radio |
| `Chinese (Mandarin)_Reliable_Executive` | 沉稳高管 | M | A | Reliable executive | Corporate, business |
| `Chinese (Mandarin)_Gentleman` | 温润男声 | M | A | Gentle, refined | Narration, storytelling |
| `Chinese (Mandarin)_Unrestrained_Young_Man` | 不羁青年 | M | Y | Unrestrained, casual | Entertainment |
| `Chinese (Mandarin)_Southern_Young_Man` | 南方小哥 | M | Y | Southern accent | Regional, casual |
| `Chinese (Mandarin)_Gentle_Youth` | 温润青年 | M | Y | Gentle young man | Narration, calm |
| `Chinese (Mandarin)_Sincere_Adult` | 真诚青年 | M | Y | Sincere, genuine | Honest, genuine |
| `Chinese (Mandarin)_Straightforward_Boy` | 率真弟弟 | M | Y | Frank, direct | Casual, direct |
| `Chinese (Mandarin)_Pure-hearted_Boy` | 清澈邻家弟弟 | M | Y | Pure-hearted neighbor | Innocent, wholesome |
| `Chinese (Mandarin)_Stubborn_Friend` | 嘴硬竹马 | M | Y | Stubborn childhood friend | Drama, character |
| `Chinese (Mandarin)_Lyrical_Voice` | 抒情男声 | M | A | Lyrical, singing | Music, singing |
| `Chinese (Mandarin)_Mature_Woman` | 傲娇御姐 | F | A | Tsundere mature woman | Romance, character |
| `Chinese (Mandarin)_Sweet_Lady` | 甜美女声 | F | A | Sweet lady | Soft, gentle |
| `Chinese (Mandarin)_Warm_Bestie` | 温暖闺蜜 | F | A | Warm bestie | Friendly, supportive |
| `Chinese (Mandarin)_Warm_Girl` | 温暖少女 | F | Y | Warm young girl | Friendly, supportive |
| `Chinese (Mandarin)_Soft_Girl` | 柔和少女 | F | Y | Soft, gentle | Calm, soothing |
| `Chinese (Mandarin)_Crisp_Girl` | 清脆少女 | F | Y | Crisp, clear | Bright, clear |
| `Chinese (Mandarin)_Gentle_Senior` | 温柔学姐 | F | Y | Gentle senior girl | Campus, supportive |
| `Chinese (Mandarin)_Wise_Women` | 阅历姐姐 | F | A | Experienced, wise | Advice, guidance |
| `Chinese (Mandarin)_HK_Flight_Attendant` | 港普空姐 | F | A | HK accent flight attendant | Regional, entertainment |
| `Chinese (Mandarin)_Cute_Spirit` | 憨憨萌兽 | N | C | Cute cartoon spirit | Animations, children's |
| `Chinese (Mandarin)_Humorous_Elder` | 搞笑大爷 | M | E | Humorous old man | Comedy, entertainment |
| `Chinese (Mandarin)_Kind-hearted_Elder` | 花甲奶奶 | F | E | Kind elderly lady | Stories, warm |
| `Chinese (Mandarin)_Kind-hearted_Antie` | 热心大婶 | F | E | Kind-hearted auntie | Warm, friendly |

### Chinese Cantonese (粤语)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Cantonese_ProfessionalHost（F)` | 专业女主持 | F | A | Professional host | Broadcasts, hosting |
| `Cantonese_GentleLady` | 温柔女声 | F | A | Gentle female | Soft, warm |
| `Cantonese_ProfessionalHost（M)` | 专业男主持 | M | A | Professional host | Broadcasts, hosting |
| `Cantonese_PlayfulMan` | 活泼男声 | M | A | Playful male | Entertainment, casual |
| `Cantonese_CuteGirl` | 可爱女孩 | F | C | Cute girl | Children's, animations |
| `Cantonese_KindWoman` | 善良女声 | F | A | Kind female | Warm, friendly |

### English

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `English_Trustworthy_Man` | Trustworthy Man | M | A | Reliable, sincere | Business, narration |
| `English_Graceful_Lady` | Graceful Lady | F | A | Elegant, refined | Formal, professional |
| `English_Aussie_Bloke` | Aussie Bloke | M | A | Casual Australian | Casual, entertainment |
| `English_Whispering_girl` | Whispering Girl | F | Y | Soft whisper | Romance, intimate |
| `English_Diligent_Man` | Diligent Man | M | A | Earnest, hardworking | Motivational, educational |
| `English_Gentle-voiced_man` | Gentle-voiced Man | M | E | Soft-spoken, kind | Calm, supportive |
| `English_Sweet_Girl` | Sweet Girl | F | C | Sweet, innocent | Children's, friendly |
| `Charming_Lady` | Charming Lady | F | A | Elegant, sophisticated | Professional, romance |
| `Attractive_Girl` | Attractive Girl | F | Y | Engaging female | Entertainment, marketing |
| `Serene_Woman` | Serene Woman | F | A | Calm, peaceful | Meditation, relaxation |
| `Santa_Claus` | Santa Claus | M | E | Festive, jolly | Holiday, children's |
| `Charming_Santa` | Charming Santa | M | E | Smooth, charismatic | Holiday, entertainment |
| `Grinch` | Grinch | M | A | Whiny, mischievous | Comedy, holiday |
| `Rudolph` | Rudolph | N | C | Cute, nasal reindeer | Children's, holiday |
| `Arnold` | Arnold | M | A | Deep, robotic | Sci-fi, action |
| `Cute_Elf` | Cute Elf | N | C | Playful, tiny elf | Fantasy, children's |

### Japanese (日本語)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Japanese_IntellectualSenior` | Intellectual Senior | M | E | Wise, knowledgeable | Narration, educational |
| `Japanese_DecisivePrincess` | Decisive Princess | F | A | Confident, royal | Animation, games |
| `Japanese_LoyalKnight` | Loyal Knight | M | A | Brave, faithful | Fantasy, games |
| `Japanese_DominantMan` | Dominant Man | M | A | Powerful, commanding | Action, leadership |
| `Japanese_SeriousCommander` | Serious Commander | M | A | Stern, authoritative | Military, games |
| `Japanese_ColdQueen` | Cold Queen | F | A | Distant, majestic | Drama, fantasy |
| `Japanese_DependableWoman` | Dependable Woman | F | A | Reliable, supportive | Guidance |
| `Japanese_GentleButler` | Gentle Butler | M | A | Polite, refined | Comedy, animation |
| `Japanese_KindLady` | Kind Lady | F | A | Warm, gentle | Comforting |
| `Japanese_CalmLady` | Calm Lady | F | A | Composed, serene | Meditation, relaxation |
| `Japanese_OptimisticYouth` | Optimistic Youth | M | Y | Cheerful, positive | Youth, motivation |
| `Japanese_GenerousIzakayaOwner` | Generous Izakaya Owner | M | A | Friendly, welcoming | Casual, comedy |
| `Japanese_SportyStudent` | Sporty Student | M | Y | Energetic, athletic | Sports, youth |
| `Japanese_InnocentBoy` | Innocent Boy | M | C | Pure, naive | Children's |
| `Japanese_GracefulMaiden` | Graceful Maiden | F | Y | Elegant, gentle | Romance, drama |

### Korean (한국어)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Korean_SweetGirl` | Sweet Girl | F | C | Sweet, adorable | Children's, romance |
| `Korean_CheerfulBoyfriend` | Cheerful Boyfriend | M | Y | Energetic, loving | Romance, dating |
| `Korean_EnchantingSister` | Enchanting Sister | F | A | Charming, captivating | Family, drama |
| `Korean_ShyGirl` | Shy Girl | F | Y | Timid, reserved | Comedy, romance |
| `Korean_ReliableSister` | Reliable Sister | F | A | Trustworthy, dependable | Guidance |
| `Korean_StrictBoss` | Strict Boss | M | A | Authoritative, demanding | Business, drama |
| `Korean_SassyGirl` | Sassy Girl | F | Y | Bold, witty | Comedy, entertainment |
| `Korean_ChildhoodFriendGirl` | Childhood Friend Girl | F | Y | Familiar, friendly | Romance, nostalgia |
| `Korean_PlayboyCharmer` | Playboy Charmer | M | A | Smooth, flirtatious | Romance, entertainment |
| `Korean_ElegantPrincess` | Elegant Princess | F | A | Graceful, royal | Animation, fantasy |
| `Korean_BraveFemaleWarrior` | Brave Female Warrior | F | A | Courageous | Action, fantasy |
| `Korean_BraveYouth` | Brave Youth | M | Y | Heroic | Action, youth |
| `Korean_CalmLady` | Calm Lady | F | A | Composed, serene | Meditation, relaxation |
| `Korean_EnthusiasticTeen` | Enthusiastic Teen | M | Y | Excited, energetic | Youth |
| `Korean_SoothingLady` | Soothing Lady | F | A | Calming, comforting | Relaxation |
| `Korean_IntellectualSenior` | Intellectual Senior | M | E | Wise, knowledgeable | Educational, narration |
| `Korean_LonelyWarrior` | Lonely Warrior | M | A | Solitary, melancholic | Drama, fantasy |
| `Korean_MatureLady` | Mature Lady | F | A | Sophisticated | Professional, drama |
| `Korean_InnocentBoy` | Innocent Boy | M | C | Pure, naive | Children's |
| `Korean_CharmingSister` | Charming Sister | F | A | Attractive, delightful | Family, romance |
| `Korean_AthleticStudent` | Athletic Student | M | Y | Sporty, energetic | Sports, youth |
| `Korean_BraveAdventurer` | Brave Adventurer | M | A | Courageous explorer | Adventure, fantasy |
| `Korean_CalmGentleman` | Calm Gentleman | M | A | Composed, refined | Formal, professional |
| `Korean_WiseElf` | Wise Elf | M | E | Ancient, mystical | Fantasy, narration |
| `Korean_CheerfulCoolJunior` | Cheerful Cool Junior | M | Y | Popular, friendly | Youth, entertainment |
| `Korean_DecisiveQueen` | Decisive Queen | F | A | Commanding | Drama, fantasy |
| `Korean_ColdYoungMan` | Cold Young Man | M | Y | Distant, aloof | Drama, romance |
| `Korean_MysteriousGirl` | Mysterious Girl | F | Y | Enigmatic, secretive | Mystery, drama |
| `Korean_QuirkyGirl` | Quirky Girl | F | Y | Eccentric, unique | Comedy |
| `Korean_ConsiderateSenior` | Considerate Senior | M | E | Thoughtful, caring | Warm, supportive |
| `Korean_CheerfulLittleSister` | Cheerful Little Sister | F | C | Playful, adorable | Family, comedy |
| `Korean_DominantMan` | Dominant Man | M | A | Powerful, commanding | Leadership, action |
| `Korean_AirheadedGirl` | Airheaded Girl | F | Y | Bubbly, spacey | Comedy |
| `Korean_ReliableYouth` | Reliable Youth | M | Y | Trustworthy, dependable | Supportive |
| `Korean_FriendlyBigSister` | Friendly Big Sister | F | A | Warm, protective | Family, support |
| `Korean_GentleBoss` | Gentle Boss | M | A | Kind, understanding | Business |
| `Korean_ColdGirl` | Cold Girl | F | Y | Aloof, distant | Drama, romance |
| `Korean_HaughtyLady` | Haughty Lady | F | A | Arrogant, proud | Drama, comedy |
| `Korean_CharmingElderSister` | Charming Elder Sister | F | A | Graceful | Romance, family |
| `Korean_IntellectualMan` | Intellectual Man | M | A | Smart, knowledgeable | Educational |
| `Korean_CaringWoman` | Caring Woman | F | A | Nurturing | Supportive, warm |
| `Korean_WiseTeacher` | Wise Teacher | M | E | Experienced | Educational |
| `Korean_ConfidentBoss` | Confident Boss | M | A | Self-assured, capable | Business, leadership |
| `Korean_AthleticGirl` | Athletic Girl | F | Y | Sporty, energetic | Sports, fitness |
| `Korean_PossessiveMan` | Possessive Man | M | A | Intense, protective | Romance, drama |
| `Korean_GentleWoman` | Gentle Woman | F | A | Soft-spoken, kind | Calm |
| `Korean_CockyGuy` | Cocky Guy | M | Y | Confident, arrogant | Comedy |
| `Korean_ThoughtfulWoman` | Thoughtful Woman | F | A | Reflective, caring | Drama |
| `Korean_OptimisticYouth` | Optimistic Youth | M | Y | Positive, hopeful | Motivation |

### Spanish (Español)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Spanish_Narrator` | Narrator | M | A | Professional narrator | Documentaries |
| `Spanish_CaptivatingStoryteller` | Captivating Storyteller | M | A | Engaging narrator | Audiobooks |
| `Spanish_WiseScholar` | Wise Scholar | M | A | Knowledgeable | Educational |
| `Spanish_SereneWoman` | Serene Woman | F | A | Calm, peaceful | Relaxation |
| `Spanish_MaturePartner` | Mature Partner | M | A | Sophisticated | Romance, drama |
| `Spanish_ConfidentWoman` | Confident Woman | F | A | Self-assured | Professional |
| `Spanish_DeterminedManager` | Determined Manager | M | A | Ambitious, driven | Business |
| `Spanish_BossyLeader` | Bossy Leader | M | A | Commanding | Leadership |
| `Spanish_ReservedYoungMan` | Reserved Young Man | M | Y | Quiet, introverted | Drama |
| `Spanish_ThoughtfulMan` | Thoughtful Man | M | A | Reflective | Educational |
| `Spanish_RationalMan` | Rational Man | M | A | Logical, analytical | Business |
| `Spanish_Deep-tonedMan` | Deep-toned Man | M | A | Deep, resonant | Commanding |
| `Spanish_Jovialman` | Jovial Man | M | A | Cheerful, friendly | Entertainment |
| `Spanish_Steadymentor` | Steady Mentor | M | A | Reliable mentor | Guidance |
| `Spanish_ReliableMan` | Reliable Man | M | A | Trustworthy | Professional |
| `Spanish_RomanticHusband` | Romantic Husband | M | A | Loving, romantic | Romance |
| `Spanish_Comedian` | Comedian | M | A | Humorous | Comedy |
| `Spanish_Debator` | Debator | M | A | Persuasive | Debate |
| `Spanish_ToughBoss` | Tough Boss | M | A | Harsh, demanding | Business, drama |
| `Spanish_AngryMan` | Angry Man | M | A | Frustrated | Drama, comedy |
| `Spanish_PowerfulSoldier` | Powerful Soldier | M | A | Strong, brave | Action, military |
| `Spanish_PassionateWarrior` | Passionate Warrior | M | A | Fierce, dedicated | Action, fantasy |
| `Spanish_PowerfulVeteran` | Powerful Veteran | M | A | Experienced | Military |
| `Spanish_SensibleManager` | Sensible Manager | M | A | Practical | Business |
| `Spanish_Kind-heartedGirl` | Kind-hearted Girl | F | C | Warm, compassionate | Children's |
| `Spanish_SophisticatedLady` | Sophisticated Lady | F | A | Elegant, refined | Formal |
| `Spanish_FrankLady` | Frank Lady | F | A | Direct, honest | Comedy |
| `Spanish_Fussyhostess` | Fussy Hostess | F | A | Demanding | Comedy, drama |
| `Spanish_Wiselady` | Wise Lady | F | E | Experienced, wise | Guidance |
| `Spanish_ThoughtfulLady` | Thoughtful Lady | F | A | Considerate | Advice |
| `Spanish_AssertiveQueen` | Assertive Queen | F | A | Commanding | Drama, fantasy |
| `Spanish_CaringGirlfriend` | Caring Girlfriend | F | Y | Nurturing | Romance |
| `Spanish_ChattyGirl` | Chatty Girl | F | Y | Talkative, sociable | Comedy |
| `Spanish_CompellingGirl` | Compelling Girl | F | Y | Persuasive | Marketing |
| `Spanish_WhimsicalGirl` | Whimsical Girl | F | C | Playful, imaginative | Children's |
| `Spanish_Intonategirl` | Intonate Girl | F | Y | Musical, melodic | Singing |
| `Spanish_SincereTeen` | Sincere Teen | M | Y | Honest, genuine | Youth |
| `Spanish_Strong-WilledBoy` | Strong-willed Boy | M | Y | Determined | Youth, motivation |
| `Spanish_EnergeticBoy` | Energetic Boy | M | C | Active, lively | Youth, sports |
| `Spanish_StrictBoss` | Strict Boss | M | A | Strict | Business |
| `Spanish_HumorousElder` | Humorous Elder | M | E | Funny | Comedy |
| `Spanish_SereneElder` | Serene Elder | M | E | Calm, peaceful | Meditation |
| `Spanish_SantaClaus` | Santa Claus | M | E | Festive | Holiday |
| `Spanish_Rudolph` | Rudolph | N | C | Reindeer | Holiday |
| `Spanish_Arnold` | Arnold | M | A | Robotic | Sci-fi |
| `Spanish_Ghost` | Ghost | N | A | Spooky | Horror |
| `Spanish_AnimeCharacter` | Anime Character | N | Y | Anime-style | Animation |

### Portuguese (Português)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Portuguese_Narrator` | Narrator | M | A | Professional narrator | Documentaries |
| `Portuguese_CaptivatingStoryteller` | Captivating Storyteller | M | A | Engaging narrator | Audiobooks |
| `Portuguese_WiseScholar` | Wise Scholar | M | A | Knowledgeable | Educational |
| `Portuguese_Deep-VoicedGentleman` | Deep-voiced Gentleman | M | A | Deep, rich | Commanding |
| `Portuguese_ReservedYoungMan` | Reserved Young Man | M | Y | Quiet, introverted | Drama |
| `Portuguese_ThoughtfulMan` | Thoughtful Man | M | A | Reflective | Educational |
| `Portuguese_RationalMan` | Rational Man | M | A | Logical | Business |
| `Portuguese_Jovialman` | Jovial Man | M | A | Cheerful | Entertainment |
| `Portuguese_Steadymentor` | Steady Mentor | M | A | Reliable mentor | Guidance |
| `Portuguese_ReliableMan` | Reliable Man | M | A | Trustworthy | Professional |
| `Portuguese_RomanticHusband` | Romantic Husband | M | A | Loving | Romance |
| `Portuguese_Comedian` | Comedian | M | A | Humorous | Comedy |
| `Portuguese_Debator` | Debator | M | A | Persuasive | Debate |
| `Portuguese_ToughBoss` | Tough Boss | M | A | Demanding | Business |
| `Portuguese_StrictBoss` | Strict Boss | M | A | Strict | Business |
| `Portuguese_AngryMan` | Angry Man | M | A | Frustrated | Drama |
| `Portuguese_Godfather` | Godfather | M | A | Authoritative | Drama |
| `Portuguese_PowerfulSoldier` | Powerful Soldier | M | A | Strong, brave | Action |
| `Portuguese_PowerfulVeteran` | Powerful Veteran | M | A | Experienced | Military |
| `Portuguese_SensibleManager` | Sensible Manager | M | A | Practical | Business |
| `Portuguese_DeterminedManager` | Determined Manager | M | A | Driven | Business |
| `Portuguese_BossyLeader` | Bossy Leader | M | A | Commanding | Leadership |
| `Portuguese_CalmLeader` | Calm Leader | M | A | Composed, steady | Leadership |
| `Portuguese_FascinatingBoy` | Fascinating Boy | M | Y | Charming | Romance |
| `Portuguese_Strong-WilledBoy` | Strong-willed Boy | M | Y | Determined | Youth |
| `Portuguese_EnergeticBoy` | Energetic Boy | M | C | Active, lively | Youth |
| `Portuguese_FragileBoy` | Fragile Boy | M | Y | Sensitive | Drama |
| `Portuguese_MaturePartner` | Mature Partner | M | A | Sophisticated | Romance |
| `Portuguese_HumorousElder` | Humorous Elder | M | E | Funny | Comedy |
| `Portuguese_SereneElder` | Serene Elder | M | E | Calm | Meditation |
| `Portuguese_ConfidentWoman` | Confident Woman | F | A | Self-assured | Professional |
| `Portuguese_SereneWoman` | Serene Woman | F | A | Calm, peaceful | Relaxation |
| `Portuguese_SentimentalLady` | Sentimental Lady | F | A | Emotional | Drama, romance |
| `Portuguese_Wiselady` | Wise Lady | F | E | Wise | Guidance |
| `Portuguese_GorgeousLady` | Gorgeous Lady | F | A | Beautiful | Romance |
| `Portuguese_LovelyLady` | Lovely Lady | F | A | Sweet, endearing | Warm |
| `Portuguese_Pompouslady` | Pompous Lady | F | A | Self-important | Comedy |
| `Portuguese_CharmingQueen` | Charming Queen | F | A | Elegant | Drama, fantasy |
| `Portuguese_AssertiveQueen` | Assertive Queen | F | A | Commanding | Drama, fantasy |
| `Portuguese_CharmingLady` | Charming Lady | F | A | Sophisticated | Professional |
| `Portuguese_InspiringLady` | Inspiring Lady | F | A | Motivating | Motivation |
| `Portuguese_StressedLady` | Stressed Lady | F | A | Anxious | Comedy |
| `Portuguese_FrankLady` | Frank Lady | F | A | Direct, honest | Comedy |
| `Portuguese_Fussyhostess` | Fussy Hostess | F | A | Demanding | Comedy |
| `Portuguese_ThoughtfulLady` | Thoughtful Lady | F | A | Considerate | Advice |
| `Portuguese_GentleTeacher` | Gentle Teacher | F | A | Kind, patient | Educational |
| `Portuguese_Kind-heartedGirl` | Kind-hearted Girl | F | C | Warm | Children's |
| `Portuguese_SweetGirl` | Sweet Girl | F | Y | Sweet, adorable | Romance |
| `Portuguese_AttractiveGirl` | Attractive Girl | F | Y | Charming | Entertainment |
| `Portuguese_PlayfulGirl` | Playful Girl | F | Y | Fun-loving | Comedy |
| `Portuguese_SmartYoungGirl` | Smart Young Girl | F | Y | Intelligent | Educational |
| `Portuguese_UpsetGirl` | Upset Girl | F | Y | Distressed | Drama |
| `Portuguese_ElegantGirl` | Elegant Girl | F | Y | Graceful | Formal |
| `Portuguese_CompellingGirl` | Compelling Girl | F | Y | Persuasive | Marketing |
| `Portuguese_WhimsicalGirl` | Whimsical Girl | F | C | Playful | Children's |
| `Portuguese_ChattyGirl` | Chatty Girl | F | Y | Talkative | Comedy |
| `Portuguese_NaughtySchoolgirl` | Naughty Schoolgirl | F | Y | Mischievous | Comedy |
| `Portuguese_SadTeen` | Sad Teen | F | Y | Melancholic | Drama |
| `Portuguese_CaringGirlfriend` | Caring Girlfriend | F | Y | Nurturing | Romance |
| `Portuguese_FriendlyNeighbor` | Friendly Neighbor | F | A | Warm, helpful | Community |
| `Portuguese_Dramatist` | Dramatist | M | A | Theatrical | Drama |
| `Portuguese_TheatricalActor` | Theatrical Actor | M | A | Dramatic | Entertainment |
| `Portuguese_Conscientiousinstructor` | Conscientious Instructor | M | A | Diligent | Training |
| `Portuguese_PlayfulSpirit` | Playful Spirit | N | C | Cheerful spirit | Fantasy |
| `Portuguese_SantaClaus` | Santa Claus | M | E | Festive | Holiday |
| `Portuguese_Rudolph` | Rudolph | N | C | Reindeer | Holiday |
| `Portuguese_Arnold` | Arnold | M | A | Robotic | Sci-fi |
| `Portuguese_CharmingSanta` | Charming Santa | M | E | Charismatic | Holiday |
| `Portuguese_Grinch` | Grinch | M | A | Mischievous | Comedy |
| `Portuguese_Ghost` | Ghost | N | A | Spooky | Horror |
| `Portuguese_GrimReaper` | Grim Reaper | N | A | Dark, ominous | Horror |

### French (Français)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `French_Male_Speech_New` | Level-Headed Man | M | A | Calm, reasonable | Professional |
| `French_Female_News Anchor` | Patient Female Presenter | F | A | Clear, patient | News |
| `French_CasualMan` | Casual Man | M | A | Relaxed, informal | Casual |
| `French_MovieLeadFemale` | Movie Lead Female | F | A | Dramatic, expressive | Drama |
| `French_FemaleAnchor` | Female Anchor | F | A | Professional anchor | News |

### Indonesian (Bahasa Indonesia)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Indonesian_SweetGirl` | Sweet Girl | F | C | Sweet, adorable | Children's |
| `Indonesian_ReservedYoungMan` | Reserved Young Man | M | Y | Quiet, introverted | Drama |
| `Indonesian_CharmingGirl` | Charming Girl | F | Y | Attractive | Romance |
| `Indonesian_CalmWoman` | Calm Woman | F | A | Composed, peaceful | Relaxation |
| `Indonesian_ConfidentWoman` | Confident Woman | F | A | Self-assured | Professional |
| `Indonesian_CaringMan` | Caring Man | M | A | Nurturing | Family |
| `Indonesian_BossyLeader` | Bossy Leader | M | A | Commanding | Leadership |
| `Indonesian_DeterminedBoy` | Determined Boy | M | Y | Ambitious | Youth |
| `Indonesian_GentleGirl` | Gentle Girl | F | Y | Soft-spoken | Calm |

### German (Deutsch)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `German_FriendlyMan` | Friendly Man | M | A | Warm, approachable | Casual |
| `German_SweetLady` | Sweet Lady | F | A | Pleasant, kind | Warm |
| `German_PlayfulMan` | Playful Man | M | A | Fun-loving | Comedy |

### Russian (Русский)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Russian_HandsomeChildhoodFriend` | Handsome Childhood Friend | M | Y | Charming | Romance |
| `Russian_BrightHeroine` | Bright Queen | F | A | Lively, strong | Drama |
| `Russian_AmbitiousWoman` | Ambitious Woman | F | A | Driven | Professional |
| `Russian_ReliableMan` | Reliable Man | M | A | Trustworthy | Professional |
| `Russian_CrazyQueen` | Crazy Girl | F | Y | Wild, unpredictable | Comedy |
| `Russian_PessimisticGirl` | Pessimistic Girl | F | Y | Gloomy | Comedy |
| `Russian_AttractiveGuy` | Attractive Guy | M | A | Charming | Romance |
| `Russian_Bad-temperedBoy` | Bad-tempered Boy | M | Y | Irritable, grumpy | Comedy |

### Italian (Italiano)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Italian_BraveHeroine` | Brave Heroine | F | A | Courageous | Action |
| `Italian_Narrator` | Narrator | M | A | Professional narrator | Storytelling |
| `Italian_WanderingSorcerer` | Wandering Sorcerer | M | A | Mysterious | Fantasy |
| `Italian_DiligentLeader` | Diligent Leader | M | A | Hardworking | Leadership |

### Arabic (العربية)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Arabic_CalmWoman` | Calm Woman | F | A | Composed | Relaxation |
| `Arabic_FriendlyGuy` | Friendly Guy | M | A | Warm | Casual |

### Turkish (Türkçe)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Turkish_CalmWoman` | Calm Woman | F | A | Composed | Relaxation |
| `Turkish_Trustworthyman` | Trustworthy Man | M | A | Reliable | Professional |

### Ukrainian (Українська)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Ukrainian_CalmWoman` | Calm Woman | F | A | Composed | Relaxation |
| `Ukrainian_WiseScholar` | Wise Scholar | M | A | Knowledgeable | Educational |

### Dutch (Nederlands)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Dutch_kindhearted_girl` | Kind-hearted Girl | F | C | Warm | Children's |
| `Dutch_bossy_leader` | Bossy Leader | M | A | Commanding | Leadership |

### Vietnamese (Tiếng Việt)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Vietnamese_kindhearted_girl` | Kind-hearted Girl | F | C | Warm | Children's |

### Thai (ภาษาไทย)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Thai_male_1_sample8` | Serene Man | M | A | Calm, peaceful | Relaxation |
| `Thai_male_2_sample2` | Friendly Man | M | A | Warm | Casual |
| `Thai_female_1_sample1` | Confident Woman | F | A | Self-assured | Professional |
| `Thai_female_2_sample2` | Energetic Woman | F | A | Active, lively | Motivation |

### Polish (Polski)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Polish_male_1_sample4` | Male Narrator | M | A | Professional | Narration |
| `Polish_male_2_sample3` | Male Anchor | M | A | Professional | News |
| `Polish_female_1_sample1` | Calm Woman | F | A | Composed | Relaxation |
| `Polish_female_2_sample3` | Casual Woman | F | A | Relaxed | Casual |

### Romanian (Română)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `Romanian_male_1_sample2` | Reliable Man | M | A | Trustworthy | Professional |
| `Romanian_male_2_sample1` | Energetic Youth | M | Y | Active, lively | Youth |
| `Romanian_female_1_sample4` | Optimistic Youth | F | Y | Positive | Motivation |
| `Romanian_female_2_sample1` | Gentle Woman | F | A | Soft-spoken | Calm |

### Greek (Ελληνικά)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `greek_male_1a_v1` | Thoughtful Mentor | M | A | Reflective, wise | Guidance |
| `Greek_female_1_sample1` | Gentle Lady | F | A | Soft-spoken | Calm |
| `Greek_female_2_sample3` | Girl Next Door | F | Y | Friendly | Casual |

### Czech (Čeština)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `czech_male_1_v1` | Assured Presenter | M | A | Confident | Presentations |
| `czech_female_5_v7` | Steadfast Narrator | F | A | Reliable | Storytelling |
| `czech_female_2_v2` | Elegant Lady | F | A | Graceful | Formal |

### Finnish (Suomi)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `finnish_male_3_v1` | Upbeat Man | M | A | Cheerful | Motivation |
| `finnish_male_1_v2` | Friendly Boy | M | Y | Warm | Children's |
| `finnish_female_4_v1` | Assertive Woman | F | A | Confident | Professional |

### Hindi (हिन्दी)

| voice_id | Name | G | Age | Description | Best For |
|----------|------|---|-----|-------------|----------|
| `hindi_male_1_v2` | Trustworthy Advisor | M | A | Reliable, wise | Guidance |
| `hindi_female_2_v1` | Tranquil Woman | F | A | Calm, peaceful | Meditation |
| `hindi_female_1_v2` | News Anchor | F | A | Professional | News |

---

## Voice Parameters

### VoiceSetting

```python
from scripts.tts.utils import VoiceSetting

voice = VoiceSetting(
    voice_id="male-qn-qingse",
    speed=1.0,       # 0.5–2.0 (default 1.0)
    volume=1.0,      # 0.1–10.0 (default 1.0)
    pitch=0,         # -12 to +12 (default 0)
    emotion="",      # Leave empty for speech-2.8 auto-matching (recommended)
)
```

### Speed

| Value | Effect |
|-------|--------|
| 0.75 | Slower, deliberate (news, tutorials) |
| 1.0 | Normal pace |
| 1.25 | Slightly faster (energetic) |
| 1.5+ | Fast (time-sensitive) |

### Emotion

| Value | Description | Model Support |
|-------|-------------|---------------|
| *(empty)* | Auto-match from text | speech-2.8 (recommended) |
| `happy` | Cheerful, upbeat | All |
| `sad` | Melancholic, somber | All |
| `angry` | Intense, frustrated | All |
| `fearful` | Anxious, nervous | All |
| `disgusted` | Repulsed | All |
| `surprised` | Astonished | All |
| `calm` | Neutral tone | All |
| `fluent` | Natural, lively | speech-2.6 only |
| `whisper` | Soft, gentle | speech-2.6 only |

---

## Custom Voices

### Voice Cloning

Create custom voices from audio samples:
- Source: 10s–5min, mp3/wav/m4a, ≤20MB, clear single speaker
- Best: 30–60s of clean speech with varied intonation

### Voice Design

Generate voices from text descriptions:
- Include: gender, age, vocal characteristics, tone, use case
- Example: "A warm, grandmotherly voice with gentle pacing, perfect for bedtime stories"

Custom voices expire after 7 days if not used with TTS. List all voices: `python scripts/tts/generate_voice.py list-voices`


path: references/video-api.md
content: # MiniMax Video Generation API Documentation

## API Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/video_generation` | POST | Create video generation task (all 4 modes) |
| `/v1/query/video_generation` | GET | Query task status |
| `/v1/files/retrieve` | GET | Get video download URL |
| `/v1/video_template_generation` | POST | Create template-based video task |
| [REDACTED_SECRET] | GET | Query template task status |

**Base URL:** `https://api.minimaxi.com`
**Auth:** `Authorization: Bearer {MINIMAX_API_KEY}`

---

## Video Generation Models

### Text-to-Video (T2V) Models
| Model | Resolution | Duration | Notes |
|-------|-----------|----------|-------|
| MiniMax-Hailuo-2.3 | 768P (default), 1080P | 6s (1080P), 6/10s (768P) | Recommended, latest |
| MiniMax-Hailuo-2.3-Fast | 768P (default), 1080P | 6s (1080P), 6/10s (768P) | Fast variant |
| MiniMax-Hailuo-02 | 512P, 768P (default), 1080P | 6s (1080P), 6/10s (512P/768P) | Previous gen |
| T2V-01-Director | 720P | 6s | Director control |
| T2V-01 | 720P | 6s | Base model |

### Image-to-Video (I2V) Models
| Model | Resolution | Duration | Notes |
|-------|-----------|----------|-------|
| MiniMax-Hailuo-2.3 | 768P, 1080P | 6s | Recommended |
| MiniMax-Hailuo-2.3-Fast | 768P, 1080P | 6s | Fast variant |
| MiniMax-Hailuo-02 | 512P, 768P, 1080P | 6/10s | Previous gen |
| I2V-01-Director | 720P | 6s | Director control |
| I2V-01-live | 720P | 6s | Live photo style |
| I2V-01 | 720P | 6s | Base model |

### Start-End Frame Model
| Model | Notes |
|-------|-------|
| MiniMax-Hailuo-02 | Only model supporting start-end frame |

### Subject Reference Model
| Model | Notes |
|-------|-------|
| S2V-01 | Face consistency across video |

---

## Request Parameters

### Common Parameters (All Modes)
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| model | string | Yes | - | Model name |
| prompt | string | Depends | - | Video description, max 2000 chars |
| duration | int | No | 6 | Video length in seconds |
| resolution | string | No | 768P/720P | Video resolution |
| prompt_optimizer | bool | No | true | Auto-optimize prompt |
| fast_pretreatment | bool | No | false | Shorten optimizer duration |
| callback_url | string | No | - | Webhook URL |
| aigc_watermark | bool | No | false | Add watermark |

### Image-to-Video Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| first_frame_image | string | Yes | Starting frame (URL or base64 data URL) |

**Image requirements:** JPG/JPEG/PNG/WebP, < 20MB, short side > 300px, aspect ratio 2:5–5:2.

### Start-End Frame Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| first_frame_image | string | Yes | Starting frame |
| last_frame_image | string | Yes | Ending frame |

### Subject Reference Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| subject_reference | array | Yes | Array of subject objects |

Each object has `type` and `image` (array of image URLs):
```json
[{ "type": "character", "image": ["<image_url>"] }]
```

---

## Camera Instructions

Supported in `[指令]` syntax for Hailuo-2.3, Hailuo-02, and Director models:

| Category | Instructions |
|----------|-------------|
| Pan | `[左移]`, `[右移]` |
| Rotation | `[左摇]`, `[右摇]` |
| Push/Pull | `[推进]`, `[拉远]` |
| Elevation | `[上升]`, `[下降]` |
| Tilt | `[上摇]`, `[下摇]` |
| Zoom | `[变焦推近]`, `[变焦拉远]` |
| Other | `[晃动]`, `[跟随]`, `[固定]` |

Combine for simultaneous: `[左摇,上升]` (max 3). Sequential: `...[推进], then ...[拉远]`

---

## Response

**Query status:** `Preparing`, `Queueing`, `Processing`, `Success`, `Fail`

**Error codes:** 0 (success), 1002 (rate limited), 1004 (auth failed), 1008 (insufficient balance), 1026 (sensitive content), 2013 (invalid params), 2049 (invalid API key)

---

## Video Templates

| Template | ID | Input | Description |
|----------|-----|-------|-------------|
| Diving | 392753057216684038 | Image | Diving motion |
| Rings | 393881433990066176 | Image | Gymnastics rings |
| Survival | 393769180141805569 | Image + Text | Outdoor survival |
| Labubu | 394246956137422856 | Image | Labubu character |
| McDonald's Delivery | 393879757702918151 | Image | Pet courier |
| Tibetan Portrait | 393766210733957121 | Image | Cultural portrait |
| Female Model Ads | 393866076583718914 | Image | Female fashion |
| Male Model Ads | 393876118804459526 | Image | Male fashion |
| Winter Romance | 393857704283172856 | Image | Snowy portrait |
| Four Seasons | 398574688191234048 | Image | Seasonal portrait |
| Helpless Moments | 394125185182695432 | Text only | Comedic animation |


path: references/video-prompt-guide.md
content: # Video Prompt Writing Guide

## Prompt Structure

### Basic Formula
**Main subject + Scene/Space + Movement/Change**

Examples:
- "A puppy runs toward the camera in a sunny park"
- "A woman walks in the rain holding an umbrella on a city street"
- "A stream flows through a green valley with morning mist"

### Professional Formula
**Main subject + Scene + Movement + Camera motion + Aesthetic atmosphere**

Examples:
- "A couple sits on a park bench, warm golden hour lighting, [固定] framing, intimate and romantic atmosphere"
- "A young man in a suit eats noodles at a street stall, [拉远] revealing the busy night market, warm tones, cinematic"
- "A dancer performs contemporary dance in an empty studio, [跟随] smooth tracking, dramatic side lighting"

---

## Key Principles

1. **More precise language → more accurate video**
2. **Richer description → better generation quality**
3. **Keep prompts focused on 5-6 seconds of action** — do not describe too many events
4. **Combine shot types with mood descriptors** for professional output

---

## Camera Instructions Usage

### Simultaneous Camera Movement
Place multiple instructions in one bracket:
- `[左摇,上升]` — pan left while rising
- `[推进,下摇]` — push in while tilting down

### Sequential Camera Movement
Place instructions at different points in the prompt:
- "The camera starts with [推进] toward the face, then [拉远] to reveal the full scene"

---

## Style-Specific Prompt Tips

### Realistic / Cinematic Style
- Mention lighting: "golden hour", "overcast sky", "dramatic side lighting"
- Color grading: "warm tones", "cool desaturated palette", "high contrast"
- Texture: "rain droplets on glass", "dust particles in sunlight"
- Cinematic terms: "shallow depth of field", "anamorphic lens flare"

### Animation Style
- Substyle: "2D anime", "3D Pixar-style", "watercolor animation", "stop-motion"
- Character design: "big expressive eyes", "chibi proportions"
- Effects: "sparkle particles", "speed lines", "dramatic wind effects"

### Product / Commercial Style
- Product details: "smooth surface", "premium materials", "elegant design"
- Studio lighting: "soft box lighting", "rim light", "gradient background"
- Motion: "slow rotation", "smooth reveal", "gentle float"

### Fantasy / Sci-Fi Style
- World elements: "floating islands", "neon cyberpunk city", "enchanted forest"
- VFX: "magic particles", "holographic displays", "energy beams"
- Scale: "vast landscape", "towering structures", "infinite horizon"

### Nature / Documentary Style
- Terminology: "macro shot", "time-lapse", "wildlife behavior"
- Phenomena: "morning dew", "sunset colors", "storm clouds"
- Precision: "slow motion at 240fps", "underwater perspective"

---

## Image-to-Video Prompt Tips

Focus on **movement and change** since the image establishes the visual:
- Image of still lake → "Gentle ripples spread across the water surface, a breeze rustles the trees, [固定] fixed camera, peaceful"
- Image of portrait → "The person slowly smiles and turns their head, natural blinking, [推进] subtle push in, warm lighting"

---

## Prompt Building Checklist

1. **Subject**: Appearance, clothing, color, expression, posture
2. **Action**: 1-2 key temporal actions ("first...then...")
3. **Scene**: Setting with foreground + background + atmosphere
4. **Camera**: `[运镜指令]` for precise control
5. **Aesthetic**: Lighting, color, texture, cinematic quality

## Common Mistakes

1. Too many events for 6-second videos
2. Conflicting camera instructions
3. Vague descriptions
4. Static descriptions without motion
5. Missing aesthetic layer
6. Overlong prompts (keep under 200 words)


path: scripts/generate_and_send.py
content: #!/usr/bin/env python3
"""
MiniMax Generate & Send — Generate multimedia content and send to Feishu.

Usage:
    python generate_and_send.py tts "Hello world" --voice female-shaonv --feishu-chat <chat_id>
    python generate_and_send.py music "soft piano" --instrumental --feishu-chat <chat_id>
    python generate_and_send.py image "A cute cat" --ratio 16:9 --feishu-chat <chat_id>
    python generate_and_send.py video "Dog running" --feishu-chat <chat_id>

After generation, prints the file path so the agent can send via feishu-media skill.
Set FEISHU_CHAT_ID env var to auto-print the feishu-media command.
"""

import argparse
import json
import os
import sys

# Add skill scripts dir to path so we can import minimax_api
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import minimax_api


def main():
    parser = argparse.ArgumentParser(description="Generate with MiniMax and prepare for Feishu send")
    parser.add_argument("type", choices=["tts", "music", "image", "i2i", "video"],
                        help="Generation type")
    parser.add_argument("prompt", help="Text prompt for generation")
    parser.add_argument("-o", "--output", default="", help="Output file path")
    parser.add_argument("--feishu-chat", default="", help="Feishu chat/group ID to send to")
    parser.add_argument("--caption", default="", help="Caption text for the message")

    # TTS args
    parser.add_argument("--voice", default="female-shaonv", help="TTS voice ID")
    parser.add_argument("--emotion", default="", help="TTS emotion")
    parser.add_argument("--tts-model", default="speech-2.8-hd", help="TTS model")

    # Music args
    parser.add_argument("--lyrics", default="", help="Music lyrics")
    parser.add_argument("--instrumental", action="store_true", help="Instrumental music")
    parser.add_argument("--music-model", default="music-2.5", help="Music model")

    # Image args
    parser.add_argument("--ratio", default="1:1", help="Image aspect ratio")
    parser.add_argument("--count", type=int, default=1, help="Number of images")
    parser.add_argument("--image-model", default="image-01", help="Image model")
    parser.add_argument("--optimize", action="store_true", help="Prompt optimizer")
    parser.add_argument("--ref-image", default="", help="Reference image for i2i")

    # Video args
    parser.add_argument("--mode", default="t2v", help="Video mode: t2v, i2v, sef, ref")
    parser.add_argument("--first-frame", default="", help="Video first frame")
    parser.add_argument("--subject", default="", help="Video subject reference")
    parser.add_argument("--video-model", default="MiniMax-Hailuo-2.3", help="Video model")
    parser.add_argument("--max-wait", type=int, default=600, help="Max wait for video")

    args = parser.parse_args()

    # Auto-generate output path
    if not args.output:
        out_dir = os.environ.get("MINIMAX_OUTPUT_DIR", "minimax-output")
        if args.type == "tts":
            ext, name = ".mp3", "tts"
        elif args.type == "music":
            ext, name = ".mp3", "music"
        elif args.type in ("image", "i2i"):
            ext, name = ".png", "image"
        elif args.type == "video":
            ext, name = ".mp4", "video"
        else:
            ext, name = ".bin", "output"
        import uuid
        short_id = uuid.uuid4().hex[:8]
        args.output = os.path.join(out_dir, "{}_{}".format(name, short_id) + ext)

    chat_id = args.feishu_chat or os.environ.get("FEISHU_CHAT_ID", "")

    try:
        result = None

        if args.type == "tts":
            result = minimax_api.generate_tts(
                args.prompt, output=args.output, voice_id=args.voice,
                emotion=args.emotion, model=args.tts_model,
            )

        elif args.type == "music":
            result = minimax_api.generate_music(
                args.prompt, output=args.output, lyrics=args.lyrics,
                instrumental=args.instrumental, model=args.music_model,
            )

        elif args.type == "image":
            result = minimax_api.generate_image(
                args.prompt, output=args.output, aspect_ratio=args.ratio,
                count=args.count, model=args.image_model,
                prompt_optimizer=args.optimize,
            )

        elif args.type == "i2i":
            if not args.ref_image:
                print("ERROR: --ref-image required for i2i mode", file=sys.stderr)
                sys.exit(1)
            result = minimax_api.image_to_image(
                args.prompt, args.ref_image, output=args.output,
                aspect_ratio=args.ratio, count=args.count,
            )

        elif args.type == "video":
            result = minimax_api.generate_video(
                args.prompt, output=args.output, mode=args.mode,
                first_frame=args.first_frame, subject_image=args.subject,
                model=args.video_model, max_wait=args.max_wait,
            )

        # Determine the actual file path(s) generated
        if isinstance(result, list):
            paths = [r.get("path", "") for r in result if r.get("path")]
        elif isinstance(result, dict):
            paths = [r for r in [result.get("path", "")] if r]
        elif isinstance(result, str):
            paths = [result]
        else:
            paths = []

        print("\n--- Generation Complete ---")
        for p in paths:
            print("File: {}".format(os.path.abspath(p)))

        if chat_id:
            print("\n--- Feishu Send ---")
            print("To send to Feishu chat '{}', use the feishu-media skill:".format(chat_id))
            for p in paths:
                print('  Send file "{}" to chat "{}"{}'.format(
                    p, chat_id,
                    ' with caption "{}"'.format(args.caption) if args.caption else "",
                ))
            # Also output machine-readable JSON for agent consumption
            print("\n__FEISHU_SEND__")
            send_info = {"chat_id": chat_id, "files": paths, "caption": args.caption}
            print(json.dumps(send_info))
        else:
            print("\nTip: Set --feishu-chat or FEISHU_CHAT_ID env var to auto-generate send commands.")

    except Exception as e:
        print("ERROR: {}".format(e), file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/minimax_api.py
content: #!/usr/bin/env python3
"""
MiniMax Multimodal API Toolkit — Python (cross-platform)

Supports: Image generation (t2i, i2i), TTS, Music, Video generation.
Requires: MINIMAX_API_KEY env var. Optional: MINIMAX_API_HOST (default: https://api.minimaxi.com).
No third-party dependencies — uses only Python standard library.
"""

from __future__ import print_function

import json
import os
import subprocess
import sys
import tempfile
import time
import urllib.parse
import urllib.request
import urllib.error
import uuid

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

DEFAULT_API_HOST = "https://api.minimaxi.com"
DEFAULT_OUTPUT_DIR = "minimax-output"


def _get_config():
    """Return (api_key, api_host, api_base). Raises if key is missing."""
    [REDACTED_SECRET]"MINIMAX_API_KEY", "")
    if not api_key:
        raise RuntimeError("MINIMAX_API_KEY environment variable is not set")
    api_host = os.environ.get("MINIMAX_API_HOST", DEFAULT_API_HOST).rstrip("/")
    return api_key, api_host, api_host + "/v1"


# ---------------------------------------------------------------------------
# HTTP helpers
# ---------------------------------------------------------------------------

def _api_request(endpoint, method="GET", body=None, timeout=120, query=None):
    """Make an API request and return the parsed JSON response."""
    api_key, _, api_base = _get_config()
    url = api_base + "/" + endpoint
    if query:
        sep = "&" if "?" in url else "?"
        url += sep + urllib.parse.urlencode(query)

    data = None
    if body is not None:
        data = json.dumps(body, ensure_ascii=False).encode("utf-8")

    req = urllib.request.Request(url, data=data, method=method)
    req.add_header("Authorization", "Bearer " + api_key)
    if body is not None:
        req.add_header("Content-Type", "application/json; charset=utf-8")

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            raw = resp.read().decode("utf-8")
    except urllib.error.HTTPError as exc:
        try:
            err_body = exc.read().decode("utf-8")
            err_json = json.loads(err_body)
            br = err_json.get("base_resp", {})
            raise RuntimeError(
                "API [{}] {} : {}".format(
                    br.get("status_code", exc.code),
                    br.get("status_msg", ""),
                    err_body[:200],
                )
            )
        except Exception:
            raise RuntimeError("HTTP {} — {}".format(exc.code, str(exc)))

    result = json.loads(raw)

    br = result.get("base_resp")
    if br and int(br.get("status_code", 0)) != 0:
        raise RuntimeError("API [{}]: {}".format(br["status_code"], br.get("status_msg", "")))
    return result


def _download_url(url, output_path):
    """Download a URL to a local file."""
    _ensure_dir(output_path)
    urllib.request.urlretrieve(url, output_path)
    return output_path


def _ensure_dir(filepath):
    """Create parent directory for filepath if needed."""
    d = os.path.dirname(filepath)
    if d:
        os.makedirs(d, exist_ok=True)


def _hex_to_file(hex_string, output_path):
    """Decode a hex string to binary and write to file."""
    _ensure_dir(output_path)
    with open(output_path, "wb") as f:
        f.write(bytes.fromhex(hex_string))
    return output_path


def _upload_file(filepath, purpose="image"):
    """Upload a file and return file_id."""
    api_key, _, api_base = _get_config()
    url = api_base + "/files/upload"

    import mimetypes
    mime = mimetypes.guess_type(filepath)[0] or "application/octet-stream"

    boundary = uuid.uuid4().hex
    with open(filepath, "rb") as f:
        file_data = f.read()
    filename = os.path.basename(filepath)

    body_parts = []
    body_parts.append(("--" + boundary).encode())
    body_parts.append(
        ('Content-Disposition: form-data; name="file"; filename="{}"'.format(filename)).encode()
    )
    body_parts.append(("Content-Type: {}".format(mime)).encode())
    body_parts.append(b"")
    body_parts.append(file_data)
    body_parts.append(("--" + boundary).encode())
    body_parts.append(b'Content-Disposition: form-data; name="purpose"')
    body_parts.append(b"")
    body_parts.append(purpose.encode())
    body_parts.append(("--" + boundary + "--").encode())
    body_parts.append(b"")

    body = b"\r\n".join(body_parts)
    req = urllib.request.Request(url, data=body, method="POST")
    req.add_header("Authorization", "Bearer " + api_key)
    req.add_header("Content-Type", "multipart/form-data; boundary=" + boundary)

    with urllib.request.urlopen(req, timeout=120) as resp:
        result = json.loads(resp.read().decode("utf-8"))

    br = result.get("base_resp")
    if br and int(br.get("status_code", 0)) != 0:
        raise RuntimeError("Upload [{}]: {}".format(br["status_code"], br.get("status_msg", "")))
    return result["data"]


# ---------------------------------------------------------------------------
# Media duration helper
# ---------------------------------------------------------------------------

def get_duration_ms(filepath):
    """Get duration of a media file in milliseconds using ffprobe if available, else raise."""
    try:
        out = subprocess.check_output(
            ["ffprobe", "-v", "error", "-show_entries", "format=duration",
             "-of", "default=noprint_wrappers=1:nokey=1", filepath],
            stderr=subprocess.STDOUT,
        )
        return int(float(out.strip()) * 1000)
    except (FileNotFoundError, subprocess.CalledProcessError, ValueError):
        raise RuntimeError("ffprobe not available or file is not a valid media file")


# ---------------------------------------------------------------------------
# TTS — POST /v1/t2a_v2
# ---------------------------------------------------------------------------

def generate_tts(
    text,
    output=None,
    voice_id="female-shaonv",
    emotion="",
    model="speech-2.8-hd",
    speed=1.0,
    volume=1.0,
    pitch=0,
    audio_format="mp3",
    sample_rate=32000,
):
    """Synthesize speech from text using MiniMax TTS.

    Args:
        text: Text to synthesize (up to 10,000 chars).
        output: Output file path (e.g. 'minimax-output/hello.mp3'). If None, returns hex string.
        voice_id: Voice ID (e.g. 'female-shaonv', 'male-qn-qingse', 'presenter_male').
        emotion: Emotion tag ('happy', 'sad', 'angry', 'fearful', 'surprised', 'calm', 'whisper', ''). Empty = auto.
        model: TTS model ('speech-2.8-hd', 'speech-2.8-turbo', 'speech-02-hd', 'speech-02-turbo').
        speed: Speech speed (0.5 - 2.0, default 1.0).
        volume: Volume (0.1 - 10.0, default 1.0).
        pitch: Pitch shift (-12 to 12, default 0).
        audio_format: Output format ('mp3', 'pcm', 'flac', 'wav').
        sample_rate: Sample rate (16000, 24000, 32000).

    Returns:
        If output is set: the output file path.
        If output is None: dict with 'hex' key.
    """
    voice_setting = {"voice_id": voice_id, "speed": speed, "vol": volume, "pitch": pitch}
    if emotion:
        voice_setting["emotion"] = emotion

    body = {
        "model": model,
        "text": text,
        "voice_setting": voice_setting,
        "audio_setting": {
            "sample_rate": sample_rate,
            "bitrate": 128000,
            "format": audio_format,
            "channel": 1,
        },
        "stream": False,
        "subtitle_enable": False,
        "output_format": "hex",
    }

    print("Synthesizing ({}): {}...".format(model, text[:60]))
    result = _api_request("t2a_v2", method="POST", body=body, timeout=120)

    audio_hex = result.get("data", {}).get("audio", "")
    if not audio_hex:
        raise RuntimeError("No audio data returned from TTS API")

    if output:
        path = _hex_to_file(audio_hex, output)
        print("  Saved: {} ({} bytes)".format(path, len(audio_hex) // 2))
        return path
    return {"hex": audio_hex}


# ---------------------------------------------------------------------------
# Music — POST /v1/music_generation (synchronous with long timeout)
# ---------------------------------------------------------------------------

def generate_music(
    prompt,
    output=None,
    lyrics="",
    instrumental=False,
    model="music-2.5",
    timeout=300,
):
    """Generate music using MiniMax Music API.

    Args:
        prompt: Music description (style, mood, instruments).
        output: Output file path. If None, returns dict with 'url'.
        lyrics: Song lyrics in structured format ('[verse]\\n...'). Required unless instrumental.
        instrumental: If True, generates background music without vocals.
        model: Music model ('music-2.5').
        timeout: HTTP timeout in seconds (music generation is slow, 30-300s).

    Returns:
        If output is set: dict with 'path' and 'url'.
        If output is None: dict with 'url'.
    """
    if instrumental:
        body = {
            "model": model,
            "prompt": prompt + ", pure music, no lyrics",
            "lyrics": "[intro]\n[outro]",
            "output_format": "url",
            "stream": False,
        }
    elif lyrics:
        body = {
            "model": model,
            "prompt": prompt,
            "lyrics": lyrics,
            "output_format": "url",
            "stream": False,
        }
    else:
        raise RuntimeError("Lyrics required for music generation (use instrumental=True for BGM)")

    print("Generating music ({}, timeout={}s)...".format(model, timeout))
    result = _api_request("music_generation", method="POST", body=body, timeout=timeout)

    audio_url = result.get("data", {}).get("audio", "")
    if not audio_url:
        raise RuntimeError("No audio URL returned from music API")

    extra = result.get("extra_info", {})
    dur_s = round(extra.get("music_duration", 0) / 1000, 1)
    size_mb = round(extra.get("music_size", 0) / 1048576, 2)
    print("  Duration: {}s, Size: {}MB".format(dur_s, size_mb))

    if output:
        path = _download_url(audio_url, output)
        print("  Saved: {}".format(path))
        return {"path": path, "url": audio_url}
    return {"url": audio_url}


# ---------------------------------------------------------------------------
# Image Generation — POST /v1/image_generation
# ---------------------------------------------------------------------------

def generate_image(
    prompt,
    output=None,
    aspect_ratio="1:1",
    count=1,
    model="image-01",
    prompt_optimizer=False,
    seed=-1,
):
    """Generate images from text prompts using MiniMax Image API.

    Args:
        prompt: Text description of the image.
        output: Output file path. If None, returns list of dicts with 'url'.
        aspect_ratio: Image aspect ratio ('1:1', '16:9', '4:3', '3:2', '2:3', '3:4', '9:16', '21:9').
        count: Number of images to generate (1-4).
        model: Image model ('image-01').
        prompt_optimizer: Enable automatic prompt optimization.
        seed: Random seed (-1 for random).

    Returns:
        If output is set: list of dicts with 'path' and 'url'.
        If output is None: list of dicts with 'url'.
    """
    body = {
        "model": model,
        "request_id": uuid.uuid4().hex,
        "prompt": prompt,
    }
    if prompt_optimizer:
        body["prompt_optimizer"] = True
    if seed >= 0:
        body["seed"] = seed
    if count > 1:
        body["n"] = count

    print("Generating image ({})...".format(model))
    result = _api_request("image_generation", method="POST", body=body, timeout=120)

    urls = result.get("data", {}).get("image_urls", [])
    if not urls:
        raise RuntimeError("No images returned from image API")

    outputs = []
    for i, img_url in enumerate(urls):
        if output:
            base, ext = os.path.splitext(output)
            if not ext:
                ext = ".png"
            suffix = "_{}".format(i + 1) if len(urls) > 1 else ""
            path = _download_url(img_url, base + suffix + ext)
            print("  Saved: {}".format(path))
            outputs.append({"path": path, "url": img_url})
        else:
            outputs.append({"url": img_url})
            print("  URL: {}".format(img_url))
    return outputs


def image_to_image(
    prompt,
    image_path,
    output=None,
    aspect_ratio="1:1",
    count=1,
    model="image-01",
    prompt_optimizer=False,
    seed=-1,
):
    """Generate images from a reference image using MiniMax Image-to-Image API.

    Args:
        prompt: Text description for the new image.
        image_path: Path or URL to the reference image.
        output: Output file path. If None, returns list of dicts with 'url'.
        aspect_ratio: Image aspect ratio.
        count: Number of images to generate (1-4).
        model: Image model ('image-01').
        prompt_optimizer: Enable automatic prompt optimization.
        seed: Random seed (-1 for random).

    Returns:
        If output is set: list of dicts with 'path' and 'url'.
        If output is None: list of dicts with 'url'.
    """
    # If image_path is a URL, use directly; otherwise base64 encode
    if image_path.startswith("http://") or image_path.startswith("https://"):
        image_file = image_path
    else:
        import base64
        with open(image_path, "rb") as f:
            b64 = base64.b64encode(f.read()).decode("ascii")
        image_file = "data:image/jpeg;base64," + b64

    body = {
        "model": model,
        "request_id": uuid.uuid4().hex,
        "prompt": prompt,
        "image_file": image_file,
    }
    if prompt_optimizer:
        body["prompt_optimizer"] = True
    if seed >= 0:
        body["seed"] = seed
    if count > 1:
        body["n"] = count

    print("Generating image-to-image ({})...".format(model))
    result = _api_request("image_generation", method="POST", body=body, timeout=120)

    urls = result.get("data", {}).get("image_urls", [])
    if not urls:
        raise RuntimeError("No images returned from image-to-image API")

    outputs = []
    for i, img_url in enumerate(urls):
        if output:
            base, ext = os.path.splitext(output)
            if not ext:
                ext = ".png"
            suffix = "_{}".format(i + 1) if len(urls) > 1 else ""
            path = _download_url(img_url, base + suffix + ext)
            print("  Saved: {}".format(path))
            outputs.append({"path": path, "url": img_url})
        else:
            outputs.append({"url": img_url})
            print("  URL: {}".format(img_url))
    return outputs


# ---------------------------------------------------------------------------
# Video Generation — POST /v1/video_generation (async) + polling
# ---------------------------------------------------------------------------

def _wait_video_task(task_id, interval=10, max_wait=600):
    """Poll video generation task until completion."""
    api_key, _, api_base = _get_config()
    elapsed = 0
    while elapsed < max_wait:
        time.sleep(interval)
        elapsed += interval
        url = api_base + "/query/video_generation?task_id=" + urllib.parse.quote(task_id)
        req = urllib.request.Request(url, method="GET")
        req.add_header("Authorization", "Bearer " + api_key)
        with urllib.request.urlopen(req, timeout=30) as resp:
            result = json.loads(resp.read().decode("utf-8"))
        status = result.get("status", "")
        print("  [{}s] Status: {}".format(elapsed, status))
        if status == "Success":
            return result
        if status == "Failed":
            raise RuntimeError("Video generation task failed")
    raise RuntimeError("Video generation timed out after {}s".format(max_wait))


def _get_video_url(file_id):
    """Retrieve download URL for a video file_id."""
    result = _api_request("files/retrieve", query={"file_id": file_id}, timeout=30)
    return result.get("file", {}).get("download_url", "")


def generate_video(
    prompt,
    output=None,
    mode="t2v",
    first_frame="",
    last_frame="",
    subject_image="",
    model="MiniMax-Hailuo-2.3",
    max_wait=600,
):
    """Generate video using MiniMax Video API.

    Args:
        prompt: Video description (subject + scene + movement + camera + aesthetic).
        output: Output file path (.mp4). If None, returns dict with 'url'.
        mode: Generation mode — 't2v' (text-to-video), 'i2v' (image-to-video),
              'sef' (start-end frame), 'ref' (subject reference).
        first_frame: Path to first frame image (for i2v and sef modes).
        last_frame: Path to last frame image (for sef mode).
        subject_image: Path to subject reference image (for ref mode).
        model: Video model ('MiniMax-Hailuo-2.3', 'MiniMax-Hailuo-2.3-Fast', 'MiniMax-Hailuo-02').
        max_wait: Max polling wait time in seconds.

    Returns:
        If output is set: dict with 'path' and 'url'.
        If output is None: dict with 'url'.
    """
    body = {"model": model, "prompt": prompt}

    if mode == "i2v" and first_frame:
        upload = _upload_file(first_frame, "image")
        body["first_frame_image"] = upload["file_id"]
    elif mode == "sef":
        if first_frame:
            upload = _upload_file(first_frame, "image")
            body["first_frame_image"] = upload["file_id"]
        if last_frame:
            upload = _upload_file(last_frame, "image")
            body["last_frame_image"] = upload["file_id"]
    elif mode == "ref" and subject_image:
        upload = _upload_file(subject_image, "image")
        body["subject_reference_image"] = upload["file_id"]

    print("Generating video ({}, {})...".format(mode, model))
    result = _api_request("video_generation", method="POST", body=body, timeout=120)

    task_id = result.get("task_id", "")
    if not task_id:
        raise RuntimeError("No task_id returned from video API")

    print("  Task: {} (polling up to {}s)...".format(task_id, max_wait))
    final = _wait_video_task(task_id, max_wait=max_wait)

    file_id = final.get("file_id", "")
    if not file_id:
        raise RuntimeError("No file_id in video result")

    video_url = _get_video_url(file_id)
    if not video_url:
        raise RuntimeError("Could not retrieve video download URL")

    w = final.get("video_width", "?")
    h = final.get("video_height", "?")
    print("  Video: {}x{}".format(w, h))

    if output:
        path = _download_url(video_url, output)
        print("  Saved: {}".format(path))
        return {"path": path, "url": video_url}
    print("  URL: {}".format(video_url))
    return {"url": video_url}


# ---------------------------------------------------------------------------
# CLI entry point
# ---------------------------------------------------------------------------

def main():
    """Simple CLI for direct invocation."""
    import argparse

    parser = argparse.ArgumentParser(description="MiniMax Multimodal API Toolkit")
    sub = parser.add_subparsers(dest="command")

    # TTS
    p = sub.add_parser("tts", help="Text-to-Speech")
    p.add_argument("text", help="Text to synthesize")
    p.add_argument("-o", "--output", default="", help="Output file path")
    p.add_argument("--voice", default="female-shaonv", help="Voice ID")
    p.add_argument("--emotion", default="", help="Emotion tag")
    p.add_argument("--model", default="speech-2.8-hd", help="TTS model")

    # Music
    p = sub.add_parser("music", help="Music generation")
    p.add_argument("prompt", help="Music description")
    p.add_argument("-o", "--output", default="", help="Output file path")
    p.add_argument("--lyrics", default="", help="Song lyrics")
    p.add_argument("--instrumental", action="store_true", help="Generate instrumental")
    p.add_argument("--model", default="music-2.5", help="Music model")

    # Image
    p = sub.add_parser("image", help="Text-to-Image")
    p.add_argument("prompt", help="Image description")
    p.add_argument("-o", "--output", default="", help="Output file path")
    p.add_argument("--ratio", default="1:1", help="Aspect ratio")
    p.add_argument("--count", type=int, default=1, help="Number of images")
    p.add_argument("--model", default="image-01", help="Image model")
    p.add_argument("--optimize", action="store_true", help="Enable prompt optimizer")

    # Image-to-Image
    p = sub.add_parser("i2i", help="Image-to-Image")
    p.add_argument("prompt", help="Image description")
    p.add_argument("image", help="Reference image path or URL")
    p.add_argument("-o", "--output", default="", help="Output file path")
    p.add_argument("--ratio", default="1:1", help="Aspect ratio")
    p.add_argument("--count", type=int, default=1, help="Number of images")

    # Video
    p = sub.add_parser("video", help="Video generation")
    p.add_argument("prompt", help="Video description")
    p.add_argument("-o", "--output", default="", help="Output file path")
    p.add_argument("--mode", default="t2v", help="Mode: t2v, i2v, sef, ref")
    p.add_argument("--first-frame", default="", help="First frame image (i2v/sef)")
    p.add_argument("--last-frame", default="", help="Last frame image (sef)")
    p.add_argument("--subject", default="", help="Subject reference image (ref)")
    p.add_argument("--model", default="MiniMax-Hailuo-2.3", help="Video model")
    p.add_argument("--max-wait", type=int, default=600, help="Max wait seconds")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    try:
        if args.command == "tts":
            out = args.output or None
            generate_tts(args.text, output=out, voice_id=args.voice,
                         emotion=args.emotion, model=args.model)
        elif args.command == "music":
            out = args.output or None
            generate_music(args.prompt, output=out, lyrics=args.lyrics,
                           instrumental=args.instrumental, model=args.model)
        elif args.command == "image":
            out = args.output or None
            generate_image(args.prompt, output=out, aspect_ratio=args.ratio,
                           count=args.count, model=args.model, prompt_optimizer=args.optimize)
        elif args.command == "i2i":
            out = args.output or None
            image_to_image(args.prompt, args.image, output=out, aspect_ratio=args.ratio,
                           count=args.count)
        elif args.command == "video":
            out = args.output or None
            generate_video(args.prompt, output=out, mode=args.mode,
                           first_frame=args.first_frame, last_frame=args.last_frame,
                           subject_image=args.subject, model=args.model,
                           max_wait=args.max_wait)
    except Exception as e:
        print("ERROR: {}".format(e), file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


path: _meta.json
content: ownerId: kn70p6rdfg6k3598at7gm6r5fd82h5rs
slug: minimax-multimodal
version: 1.0.1
publishedAt: 1774394943918

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)