Initial commit with translated description
This commit is contained in:
117
SKILL.md
Normal file
117
SKILL.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
name: ai-notes-ofvideo
|
||||||
|
description: "从视频生成AI驱动的笔记(文档、大纲或图文格式)。"
|
||||||
|
metadata: { "openclaw": { "emoji": "📺", "requires": { "bins": ["python3"], "env":["BAIDU_API_KEY"]},"primaryEnv":"BAIDU_API_KEY" } }
|
||||||
|
---
|
||||||
|
|
||||||
|
# AI Video Notes
|
||||||
|
|
||||||
|
Generate structured notes from video URLs using Baidu AI. Supports three note formats.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Create Task**: Submit video URL → get task ID
|
||||||
|
2. **Poll Status**: Query task every 3-5 seconds until completion
|
||||||
|
3. **Get Results**: Retrieve generated notes when status = 10002
|
||||||
|
|
||||||
|
## Status Codes
|
||||||
|
|
||||||
|
| Code | Status | Action |
|
||||||
|
|-------|---------|---------|
|
||||||
|
| 10000 | In Progress | Continue polling |
|
||||||
|
| 10002 | Completed | Return results |
|
||||||
|
| Other | Failed | Show error |
|
||||||
|
|
||||||
|
## Note Types
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| 1 | Document notes |
|
||||||
|
| 2 | Outline notes |
|
||||||
|
| 3 | Graphic-text notes |
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
### Create Task
|
||||||
|
|
||||||
|
**Endpoint**: `POST /v2/tools/ai_note/task_create`
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `video_url` (required): Public video URL
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```bash
|
||||||
|
python3 scripts/ai_notes_task_create.py 'https://example.com/video.mp4'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"task_id": "uuid-string"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Query Task
|
||||||
|
|
||||||
|
**Endpoint**: `GET /v2/tools/ai_note/query`
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `task_id` (required): Task ID from create endpoint
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```bash
|
||||||
|
python3 scripts/ai_notes_task_query.py "task-id-here"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response** (Completed):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 10002,
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"tpl_no": "1",
|
||||||
|
"contents: ["Note content..."]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Polling Strategy
|
||||||
|
|
||||||
|
### Option 1: Manual Polling
|
||||||
|
1. Create task → store `task_id`
|
||||||
|
2. Query every 3-5 seconds:
|
||||||
|
```bash
|
||||||
|
python3 scripts/ai_notes_task_query.py <task_id>
|
||||||
|
```
|
||||||
|
3. Show progress updates:
|
||||||
|
- Status 10000: Processing...
|
||||||
|
- Status 10002: Completed
|
||||||
|
4. Stop after 30-60 seconds (video length dependent)
|
||||||
|
|
||||||
|
### Option 2: Auto Polling (Recommended)
|
||||||
|
Use the polling script for automatic status updates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/ai_notes_poll.py <task_id> [max_attempts] [interval_seconds]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Default: 20 attempts, 3-second intervals
|
||||||
|
python3 scripts/ai_notes_poll.py "task-id-here"
|
||||||
|
|
||||||
|
# Custom: 30 attempts, 5-second intervals
|
||||||
|
python3 scripts/ai_notes_poll.py "task-id-here" 30 5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
- Shows real-time progress: `[1/20] Processing... 25%`
|
||||||
|
- Auto-stops when complete
|
||||||
|
- Returns formatted notes with type labels
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- Invalid URL: "Video URL not accessible"
|
||||||
|
- Processing error: "Failed to parse video"
|
||||||
|
- Timeout: "Video too long, try again later"
|
||||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ownerId": "kn7akgt520t01vgs2tzx7yk6m180kt26",
|
||||||
|
"slug": "ai-notes-ofvideo",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"publishedAt": 1770955841859
|
||||||
|
}
|
||||||
133
scripts/ai_notes_poll.py
Normal file
133
scripts/ai_notes_poll.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
AI Video Notes - 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 = {
|
||||||
|
10000: "processing",
|
||||||
|
10002: "completed",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def query_task(api_key: str, task_id: str) -> Dict[str, Any]:
|
||||||
|
"""Query task status."""
|
||||||
|
url = "https://qianfan.baidubce.com/v2/tools/ai_note/query"
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {api_key}",
|
||||||
|
"X-Appbuilder-From": "openclaw",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
params = {"task_id": task_id}
|
||||||
|
|
||||||
|
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def poll_task(api_key: str, task_id: str, max_attempts: int = 20, interval: int = 3):
|
||||||
|
"""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 3)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Final task data
|
||||||
|
"""
|
||||||
|
data = None
|
||||||
|
for attempt in range(max_attempts):
|
||||||
|
try:
|
||||||
|
data = query_task(api_key, task_id)
|
||||||
|
if "errno" in data and data["errno"] == 10000:
|
||||||
|
errno = data["errno"]
|
||||||
|
error_msg = data["show_msg"]
|
||||||
|
print(f"[{attempt + 1}/{max_attempts}] Processing...(task status code: {errno}, message: {error_msg})")
|
||||||
|
time.sleep(interval)
|
||||||
|
continue
|
||||||
|
if "errno" in data and data["errno"] != 0:
|
||||||
|
raise RuntimeError(data.get("show_msg", "Unknown error"))
|
||||||
|
data = data.get("data", {})
|
||||||
|
aiNotesList = data.get("list", [])
|
||||||
|
status_code = 0
|
||||||
|
for note in aiNotesList:
|
||||||
|
status_code = note.get("detail", {}).get("status", 0)
|
||||||
|
if status_code != 10002:
|
||||||
|
break
|
||||||
|
|
||||||
|
if status_code == 10002:
|
||||||
|
notes = []
|
||||||
|
for note in data.get("list", []):
|
||||||
|
tpl_no = note.get("tpl_no")
|
||||||
|
notes.append({
|
||||||
|
"type": tpl_no,
|
||||||
|
"contents": note.get("detail", {}).get("contents", [])
|
||||||
|
})
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print("✓ NOTES GENERATED SUCCESSFULLY")
|
||||||
|
print("=" * 50)
|
||||||
|
print(json.dumps(notes, indent=2, ensure_ascii=False))
|
||||||
|
return data
|
||||||
|
|
||||||
|
elif status_code == 10000:
|
||||||
|
print(f"[{attempt + 1}/{max_attempts}] Processing...")
|
||||||
|
time.sleep(interval)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
print(f"\n✗ Task failed, with status code: {status_code}")
|
||||||
|
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_notes_task_query.py {task_id}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(json.dumps({
|
||||||
|
"error": "Missing task ID",
|
||||||
|
"usage": "python ai_notes_poll.py <task_id> [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 3
|
||||||
|
|
||||||
|
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()
|
||||||
94
scripts/ai_notes_task_create.py
Normal file
94
scripts/ai_notes_task_create.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
AI Video Notes - Create Task
|
||||||
|
Submit a video URL for AI note generation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
def create_note_task(api_key: str, video_url: str) -> Dict[str, Any]:
|
||||||
|
"""Create an AI note generation task.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
api_key: Baidu API key
|
||||||
|
video_url: Public video URL
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Task data with task_id
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: If API returns error
|
||||||
|
"""
|
||||||
|
url = "https://qianfan.baidubce.com/v2/tools/ai_note/task_create"
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {api_key}",
|
||||||
|
"X-Appbuilder-From": "openclaw",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
data = {"url": video_url}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if "code" in result:
|
||||||
|
raise RuntimeError(result.get("detail", "API error"))
|
||||||
|
if "errno" in result and result["errno"] != 0:
|
||||||
|
raise RuntimeError(result.get("errmsg", "Unknown error"))
|
||||||
|
|
||||||
|
return result["data"]
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
raise RuntimeError("Request timeout. Video URL may be inaccessible.")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise RuntimeError(f"Network error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(json.dumps({
|
||||||
|
"error": "Missing video URL",
|
||||||
|
"usage": "python ai_notes_task_create.py <video_url>"
|
||||||
|
}, indent=2))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
video_url = sys.argv[1]
|
||||||
|
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)
|
||||||
|
|
||||||
|
try:
|
||||||
|
task_data = create_note_task(api_key, video_url)
|
||||||
|
print(json.dumps({
|
||||||
|
"status": "success",
|
||||||
|
"message": "Task created successfully",
|
||||||
|
"task_id": task_data.get("task_id"),
|
||||||
|
"next_step": f"Query task status: python ai_notes_task_query.py {task_data.get('task_id')}"
|
||||||
|
}, indent=2))
|
||||||
|
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(json.dumps({
|
||||||
|
"status": "error",
|
||||||
|
"error": str(e)
|
||||||
|
}, indent=2))
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({
|
||||||
|
"status": "error",
|
||||||
|
"error": f"Unexpected error: {str(e)}"
|
||||||
|
}, indent=2))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
45
scripts/ai_notes_task_query.py
Normal file
45
scripts/ai_notes_task_query.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def ai_notes_task_query(api_key: str, task_id: str):
|
||||||
|
url = "https://qianfan.baidubce.com/v2/tools/ai_note/query"
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer %s" % api_key,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
"task_id": task_id,
|
||||||
|
}
|
||||||
|
response = requests.get(url, headers=headers, params=params)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
if "code" in result:
|
||||||
|
raise RuntimeError(result["detail"])
|
||||||
|
if "errno" in result and result["errno"] == 10000:
|
||||||
|
print("task status code: %s, message: %s" % (result["errno"], result["show_msg"]))
|
||||||
|
return {}
|
||||||
|
if "errno" in result and result["errno"] != 0:
|
||||||
|
raise RuntimeError(result["show_msg"])
|
||||||
|
return result.get("data", {})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python ai_notes_task_create.py <url>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
task_id = 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_notes_task_query(api_key, task_id)
|
||||||
|
print(json.dumps(results, ensure_ascii=False, indent=2))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {str(e)}")
|
||||||
|
sys.exit(1)
|
||||||
Reference in New Issue
Block a user