From bff465f5c63d7a2d84f77f70df6f3ea5545253cf Mon Sep 17 00:00:00 2001 From: zlei9 Date: Sun, 29 Mar 2026 09:46:52 +0800 Subject: [PATCH] Initial commit with translated description --- SKILL.md | 100 ++++++++++++++++++++ _meta.json | 6 ++ scripts/ai_picture_book_poll.py | 125 +++++++++++++++++++++++++ scripts/ai_picture_book_task_create.py | 49 ++++++++++ scripts/ai_picture_book_task_query.py | 52 ++++++++++ 5 files changed, 332 insertions(+) create mode 100644 SKILL.md create mode 100644 _meta.json create mode 100644 scripts/ai_picture_book_poll.py create mode 100644 scripts/ai_picture_book_task_create.py create mode 100644 scripts/ai_picture_book_task_query.py diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..07bce74 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,100 @@ +--- +name: ai-picture-book +description: "使用百度AI生成静态或动态绘本视频。" +metadata: { "openclaw": { "emoji": "📔", "requires": { "bins": ["python3"], "env":["BAIDU_API_KEY"]},"primaryEnv":"BAIDU_API_KEY" } } +--- + +# AI Picture Book + +Generate picture book videos from stories or descriptions. + +## Workflow + +1. **Create Task**: Submit story + type → get task ID +2. **Poll Status**: Query every 5-10s until completion +3. **Get Results**: Retrieve video URLs when status = 2 + +## Book Types + +| Type | Method | Description | +|------|--------|-------------| +| Static | 9 | Static picture book | +| Dynamic | 10 | Dynamic picture book | + +**Required**: User must specify type (static/9 or dynamic/10). If not provided, ask them to choose. + +## Status Codes + +| Code | Status | Action | +|-------|---------|---------| +| 0, 1, 3 | In Progress | Continue polling | +| 2 | Completed | Return results | +| Other | Failed | Show error | + +## APIs + +### Create Task + +**Endpoint**: `POST /v2/tools/ai_picture_book/task_create` + +**Parameters**: +- `method` (required): `9` for static, `10` for dynamic +- `content` (required): Story or description + +**Example**: +```bash +python3 scripts/ai_picture_book_task_create.py 9 "A brave cat explores the world." +``` + +**Response**: +```json +{ "task_id": "uuid-string" } +``` + +### Query Task + +**Endpoint**: `GET /v2/tools/ai_picture_book/query` + +**Parameters**: +- `task_id` (required): Task ID from create endpoint + +**Example**: +```bash +python3 scripts/ai_picture_book_task_query.py "task-id-here" +``` + +**Response** (Completed): +```json +{ + "status": 2, + "video_bos_url": "https://...", +} +``` + +## Polling Strategy + +### Auto Polling (Recommended) +```bash +python3 scripts/ai_picture_book_poll.py [max_attempts] [interval_seconds] +``` + +**Examples**: +```bash +# Default: 20 attempts, 5s intervals +python3 scripts/ai_picture_book_poll.py "task-id-here" + +# Custom: 30 attempts, 10s intervals +python3 scripts/ai_picture_book_poll.py "task-id-here" 30 10 +``` + +### Manual Polling +1. Create task → store `task_id` +2. Query every 5-10s until status = 2 +3. Timeout after 2-3 minutes + +## Error Handling + +- Invalid content: "Content cannot be empty" +- Invalid type: "Invalid type. Use 9 (static) or 10 (dynamic)" +- Processing error: "Failed to generate picture book" +- Timeout: "Task timed out. Try again later" diff --git a/_meta.json b/_meta.json new file mode 100644 index 0000000..4e33ed1 --- /dev/null +++ b/_meta.json @@ -0,0 +1,6 @@ +{ + "ownerId": "kn7akgt520t01vgs2tzx7yk6m180kt26", + "slug": "ai-picture-book", + "version": "1.1.0", + "publishedAt": 1770956044079 +} \ No newline at end of file diff --git a/scripts/ai_picture_book_poll.py b/scripts/ai_picture_book_poll.py new file mode 100644 index 0000000..b810b80 --- /dev/null +++ b/scripts/ai_picture_book_poll.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +AI Picture Book - Poll Task +Automatically poll task until completion with progress updates. +""" + +import os +import sys +import json +import time +import requests +from typing import Dict, Any + +STATUS_CODES = { + 0: "in_progress", + 1: "in_progress", + 2: "completed", + 3: "in_progress", +} + + +def query_task(api_key: str, task_id: str) -> Dict[str, Any]: + """Query task status.""" + url = "https://qianfan.baidubce.com/v2/tools/ai_picture_book/query" + headers = { + "Authorization": f"Bearer {api_key}", + "X-Appbuilder-From": "openclaw", + "Content-Type": "application/json" + } + params = {"task_ids": [task_id]} + + response = requests.post(url, headers=headers, json=params, timeout=5) + response.raise_for_status() + result = response.json() + + if "errno" in result and result["errno"] != 0: + raise RuntimeError(result.get("errmsg", "Unknown error")) + + return result["data"] + + +def poll_task(api_key: str, task_id: str, max_attempts: int = 20, interval: int = 5): + """Poll task until completion or timeout. + + Args: + api_key: Baidu API key + task_id: Task ID + max_attempts: Maximum poll attempts (default 20) + interval: Seconds between polls (default 5) + + Returns: + Final task data + """ + data = None + for attempt in range(max_attempts): + try: + data = query_task(api_key, task_id) + if not data or len(data) == 0: + print(f"\n✗ No task data returned") + return data + + status = data[0].get("status") + result = data[0].get("result", {}) + if result and "video_bos_url" in result: + result = {"video_bos_url": result["video_bos_url"]} + + if status == 2: + print("\n" + "=" * 50) + print("✓ PICTURE BOOK GENERATED SUCCESSFULLY") + print("=" * 50) + print(json.dumps(result, indent=2, ensure_ascii=False)) + return data + + elif status in [0, 1, 3]: + print(f"[{attempt + 1}/{max_attempts}] Processing...") + time.sleep(interval) + + else: + print(f"\n✗ Task failed: status={status}") + print(json.dumps(data, indent=2, ensure_ascii=False)) + return data + + except RuntimeError as e: + print(f"\n✗ Error: {str(e)}") + return data + except Exception as e: + if attempt == max_attempts - 1: + print(f"\n✗ Unexpected error: {str(e)}") + return data + time.sleep(interval) + + print(f"\n✗ Timeout after {max_attempts * interval} seconds") + print("Task may still be running. Try querying manually:") + print(f" python scripts/ai_picture_book_task_query.py {task_id}") + return data + + +def main(): + if len(sys.argv) < 2: + print(json.dumps({ + "error": "Missing task ID", + "usage": "python ai_picture_book_poll.py [max_attempts] [interval_seconds]" + }, indent=2)) + sys.exit(1) + + task_id = sys.argv[1] + max_attempts = int(sys.argv[2]) if len(sys.argv) > 2 else 20 + interval = int(sys.argv[3]) if len(sys.argv) > 3 else 5 + + api_key = os.getenv("BAIDU_API_KEY") + if not api_key: + print(json.dumps({ + "error": "BAIDU_API_KEY environment variable not set" + }, indent=2)) + sys.exit(1) + + print(f"Polling task: {task_id}") + print(f"Max attempts: {max_attempts}, Interval: {interval}s") + print("-" * 50) + + poll_task(api_key, task_id, max_attempts, interval) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai_picture_book_task_create.py b/scripts/ai_picture_book_task_create.py new file mode 100644 index 0000000..7b9cfae --- /dev/null +++ b/scripts/ai_picture_book_task_create.py @@ -0,0 +1,49 @@ +import os +import sys +import requests +import json + + +def ai_picture_book_task_create(api_key: str, method: int, content): + url = "https://qianfan.baidubce.com/v2/tools/ai_picture_book/task_create" + headers = { + "Authorization": "Bearer %s" % api_key, + "X-Appbuilder-From": "openclaw", + "Content-Type": "application/json" + } + params = { + "method": method, + "input_type": "1", + "input_content": content, + } + response = requests.post(url, headers=headers, json=params) + response.raise_for_status() + result = response.json() + if "code" in result: + raise RuntimeError(result["detail"]) + if "errno" in result and result["errno"] != 0: + raise RuntimeError(result["errmsg"]) + return result["data"] + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python ai_picture_book_task_create.py ") + sys.exit(1) + + method = int(sys.argv[1]) + if method not in [9, 10]: + print("Error: method must be 9 or 10.") + sys.exit(1) + content = sys.argv[2] + + api_key = os.getenv("BAIDU_API_KEY") + if not api_key: + print("Error: BAIDU_API_KEY must be set in environment.") + sys.exit(1) + try: + results = ai_picture_book_task_create(api_key, method, content) + print(json.dumps(results, ensure_ascii=False, indent=2)) + except Exception as e: + print(f"Error: {str(e)}") + sys.exit(1) diff --git a/scripts/ai_picture_book_task_query.py b/scripts/ai_picture_book_task_query.py new file mode 100644 index 0000000..343a728 --- /dev/null +++ b/scripts/ai_picture_book_task_query.py @@ -0,0 +1,52 @@ +import os +import sys +import requests +import json + + +def ai_picture_book_task_query(api_key: str, task_id: str): + url = "https://qianfan.baidubce.com/v2/tools/ai_picture_book/query" + headers = { + "Authorization": "Bearer %s" % api_key, + "X-Appbuilder-From": "openclaw", + "Content-Type": "application/json" + } + task_ids = task_id.split(",") + params = { + "task_ids": task_ids, + } + response = requests.post(url, headers=headers, json=params) + response.raise_for_status() + result = response.json() + datas = [] + if "code" in result: + raise RuntimeError(result["detail"]) + if "errno" in result and result["errno"] != 0: + raise RuntimeError(result["errmsg"]) + if "data" in result and len(result["data"]) > 0: + for item in result["data"]: + if item["result"]: + bosUrl = item["result"].get("video_bos_url", "") + del item["result"] + item["video_bos_url"] = bosUrl + datas.append(item) + return datas + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python ai_picture_book_task_query.py ") + sys.exit(1) + + task_ids = sys.argv[1] + + api_key = os.getenv("BAIDU_API_KEY") + if not api_key: + print("Error: BAIDU_API_KEY must be set in environment.") + sys.exit(1) + try: + results = ai_picture_book_task_query(api_key, task_ids) + print(json.dumps(results, ensure_ascii=False, indent=2)) + except Exception as e: + print(f"Error: {str(e)}") + sys.exit(1)