sophhub 0.4.11 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/bing-image-search/skill.json +20 -0
- package/skills/bing-image-search/src/SKILL.md +105 -0
- package/skills/flight-booking/skill.json +12 -3
- package/skills/flight-booking/src/SKILL.md +4 -4
- package/skills/flight-booking/src/scripts/flight_booking.py +9 -5
- package/skills/sophnet-video-generate/skill.json +20 -3
- package/skills/sophnet-video-generate/src/SKILL.md +10 -9
- package/skills/sophnet-video-generate/src/scripts/gen_video.py +48 -31
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bing-image-search",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"types": [
|
|
5
|
+
"store"
|
|
6
|
+
],
|
|
7
|
+
"displayName": "Bing 图片搜索",
|
|
8
|
+
"description": "通过 Bing 抓取图片原图 URL 并并行 HEAD 校验可用性,直接以 Markdown 行内图片语法嵌入预览;不下载、不上传、不经过图像生成。",
|
|
9
|
+
"changelog": [
|
|
10
|
+
{
|
|
11
|
+
"version": "1.0.0",
|
|
12
|
+
"date": "2026-05-07",
|
|
13
|
+
"changes": [
|
|
14
|
+
"初次提交"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"createdAt": "2026-05-07",
|
|
19
|
+
"updatedAt": "2026-05-07"
|
|
20
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bing-image-search
|
|
3
|
+
description: |
|
|
4
|
+
通过 Bing 图片搜索快速搜图,并行 HEAD 校验 URL 可用性后以 Markdown 行内图片语法直接嵌入预览。
|
|
5
|
+
当用户说"找图片"、"搜图"、"发几张XX图"、"给我看看XX照片"、"找一些XX相关的图片"时使用。
|
|
6
|
+
不要用 web_search 搜图(返回文本不含图片URL),直接用本 skill 的 curl 方案。
|
|
7
|
+
homepage: https://cn.bing.com/images/
|
|
8
|
+
metadata: { "openclaw": { "emoji": "🖼️", "requires": { "bins": ["curl", "grep", "xargs", "awk"] } } }
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Bing 图片搜索与预览
|
|
12
|
+
|
|
13
|
+
## 核心流程
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Bing 图片搜索 → 提取 murl → 并行 HEAD 校验 →  嵌入 → 发出
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
一步到位,不下载、不上传、不经过图像生成。
|
|
20
|
+
|
|
21
|
+
## 一键脚本:搜图 + 并行校验
|
|
22
|
+
|
|
23
|
+
把搜图和校验写在同一个 exec 里。**校验是并行的**(`xargs -P 10`),整轮耗时约等于单次超时(~3s),与 `N` 几乎无关:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
N=3 # 需要张数
|
|
27
|
+
QUERY="cute cats" # 关键词,建议英文;含中文时需 URL 编码
|
|
28
|
+
POOL=20 # 候选池;越大越鲁棒,并行校验不会变慢
|
|
29
|
+
|
|
30
|
+
# 1) 抓 Bing 图片搜索结果,提取去重后的原图 URL(最多 POOL 个)
|
|
31
|
+
URLS=$(curl -sL --max-time 10 \
|
|
32
|
+
"https://cn.bing.com/images/search?q=${QUERY// /+}&first=0&count=${POOL}" \
|
|
33
|
+
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36' \
|
|
34
|
+
| grep -oP 'murl":"\K[^&"]+\.(jpg|jpeg|png|webp)[^&"]*' \
|
|
35
|
+
| awk '!seen[$0]++' \
|
|
36
|
+
| head -n "$POOL")
|
|
37
|
+
|
|
38
|
+
# 2) 并行 HEAD 校验,只输出可达 URL;取前 N 个即可
|
|
39
|
+
# stderr 重定向是为了消掉 head 提前关闭管道时 xargs 的 SIGPIPE 噪声
|
|
40
|
+
printf '%s\n' "$URLS" | xargs -P 10 -I{} bash -c '
|
|
41
|
+
url="$1"
|
|
42
|
+
code=$(curl -sI -L --max-redirs 3 --connect-timeout 2 --max-time 3 \
|
|
43
|
+
-H "Referer: https://cn.bing.com/" \
|
|
44
|
+
-H "User-Agent: Mozilla/5.0" \
|
|
45
|
+
-o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
|
|
46
|
+
[ "$code" = "200" ] && printf "%s\n" "$url"
|
|
47
|
+
' _ {} 2>/dev/null | head -n "$N"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
输出每行一个 URL,直接拿来嵌入即可。
|
|
51
|
+
|
|
52
|
+
## 仅搜图(不校验)
|
|
53
|
+
|
|
54
|
+
调试或仅需原始候选列表时:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl -sL --max-time 10 \
|
|
58
|
+
"https://cn.bing.com/images/search?q=cute+cats&first=0&count=20" \
|
|
59
|
+
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36' \
|
|
60
|
+
| grep -oP 'murl":"\K[^&"]+\.(jpg|jpeg|png|webp)[^&"]*' \
|
|
61
|
+
| awk '!seen[$0]++' | head -n 10
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 仅校验(已有 URL 列表)
|
|
65
|
+
|
|
66
|
+
把 URL 一行一个传给:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
xargs -P 10 -I{} bash -c '
|
|
70
|
+
url="$1"
|
|
71
|
+
code=$(curl -sI -L --max-redirs 3 --connect-timeout 2 --max-time 3 \
|
|
72
|
+
-H "Referer: https://cn.bing.com/" -H "User-Agent: Mozilla/5.0" \
|
|
73
|
+
-o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
|
|
74
|
+
[ "$code" = "200" ] && printf "%s\n" "$url"
|
|
75
|
+
' _ {} 2>/dev/null
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 嵌入预览
|
|
79
|
+
|
|
80
|
+
拿到校验通过的 URL 后,每行一条:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+

|
|
84
|
+

|
|
85
|
+

|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 性能要点
|
|
89
|
+
|
|
90
|
+
- **并行校验**:从 N 张图近 `N × max-time` 的串行耗时降到 `max-time` 量级(~3s)。`POOL=20`、`-P 10` 是经验上的甜点。
|
|
91
|
+
- **`--connect-timeout 2`**:与 `--max-time 3` 配合,避免 DNS / 三次握手卡满整个 budget。
|
|
92
|
+
- **`grep -oP '...\K...'`**:用 PCRE lookbehind 直接得到捕获,省掉 `sed` 管道。
|
|
93
|
+
- **`awk '!seen[$0]++'`**:先去重再校验,防止候选池里重复 URL 浪费请求。
|
|
94
|
+
- **`Referer: cn.bing.com`**:很多图床只允许"来自搜索引擎"的请求,加了能显著降低裂图率。
|
|
95
|
+
- **`head -n "$N"`**:取够即停;上游 `xargs` 会因 SIGPIPE 自动收敛。
|
|
96
|
+
|
|
97
|
+
## 注意事项
|
|
98
|
+
|
|
99
|
+
1. **不要下载图片到本地** — 聊天直接用源站 URL 嵌入,不落盘。
|
|
100
|
+
2. **不要用 web_search 搜图** — 返回文本摘要,不含图片 URL。
|
|
101
|
+
3. **必须校验** — 跳过 HEAD 校验直接嵌 URL,防盗链域名会导致裂图。
|
|
102
|
+
4. **关键词优先英文** — Bing 图库英文关键词结果质量和数量都更好;中文关键词请先 URL 编码(`jq -rR @uri` 或 `python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' "$QUERY"`)。
|
|
103
|
+
5. **User-Agent 必须带** — Bing 会按 UA 节流,不带的请求量太少。
|
|
104
|
+
6. **依赖 GNU grep 的 `-P`** — Linux 默认满足;macOS 需 `brew install grep` 后用 `ggrep`,或换用 `pcregrep`。
|
|
105
|
+
7. **若所有候选都校验失败** — 增大 `POOL` 重试,或换关键词;少数图床会拒绝 `HEAD`,必要时改成 `curl -sL -r 0-0 -o /dev/null -w '%{http_code}'` 用 1-byte GET 兜底。
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flight-booking",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"types": [
|
|
5
5
|
"builtin"
|
|
6
6
|
],
|
|
7
7
|
"displayName": "国内机票预定",
|
|
8
|
-
"description": "",
|
|
8
|
+
"description": "国内机票查询、下单、出票、退票、改签全流程 skill",
|
|
9
9
|
"changelog": [
|
|
10
|
+
{
|
|
11
|
+
"changes": [
|
|
12
|
+
"search 取消前 15 条截断,返回当日全部航班并在表格末尾新增汇总行",
|
|
13
|
+
"list_flights_simple 默认 top_n 由 15 改为 None(返回全部)",
|
|
14
|
+
"SKILL.md 强化指引:严禁截断或只展示前 N 条航班,须逐条全部列出"
|
|
15
|
+
],
|
|
16
|
+
"date": "2026-05-09",
|
|
17
|
+
"version": "1.2.0"
|
|
18
|
+
},
|
|
10
19
|
{
|
|
11
20
|
"changes": [
|
|
12
21
|
"修正票务网关基址为 https://www.sophnet.com/api,避免请求落到 /v1/api 导致 405 或非 JSON 响应"
|
|
@@ -23,5 +32,5 @@
|
|
|
23
32
|
}
|
|
24
33
|
],
|
|
25
34
|
"createdAt": "2026-04-09",
|
|
26
|
-
"updatedAt": "2026-
|
|
35
|
+
"updatedAt": "2026-05-09"
|
|
27
36
|
}
|
|
@@ -16,13 +16,13 @@ description: "中国国内机票查询与预订。当用户需要查航班、买
|
|
|
16
16
|
- **出票前务必再次确认**:**不得在创建订单后自动连续执行出票**。第二步创建订单完成后,应先向用户展示订单号、航程与金额,**在用户明确表示「确认出票」或「去支付/出票」后再执行第三步 pay-issue**;未得到用户确认前,不要主动执行出票。
|
|
17
17
|
- **退票前务必二次确认**:**绝对不得在用户未明确确认的情况下执行退票。** 退票流程必须分两步:第一步用 `--dry-run` 查询并向用户展示退改签规则和手续费,第二步**必须等用户明确说「确认退票」「退吧」等确认性语句后**才执行实际退票命令。即使用户说了「我想退票」「帮我看看退票」,也只能先执行 dry-run 展示规则,**不得直接执行退票**。
|
|
18
18
|
- **用户必填变量**:仅需通过**环境变量**配置乘机人 `name`、`mobile`、`credentialNo`、`gender`。联系人默认与乘机人一致,无需重复设置。
|
|
19
|
-
-
|
|
19
|
+
- **航班查询结果展示须完整**:向用户展示航班列表时,**必须展示脚本返回的全部航班,不得截断、不得只展示前 N 条、不得"为节省篇幅"省略任何一条**。脚本已不再做条数限制,返回多少条就展示多少条(即使有几十条也必须全部列出)。每条须包含航班号、航空公司、出发/到达机场、**起飞时间**、**到达时间**(两列分开)、**经济舱价格与公务舱价格**、**机建费**、**燃油费**,不得漏列(无公务舱时显示「—」)。
|
|
20
20
|
|
|
21
21
|
## 流程概览
|
|
22
22
|
|
|
23
23
|
| 步骤 | 说明 | 脚本子命令 |
|
|
24
24
|
|------|------|------------|
|
|
25
|
-
| 第一步 |
|
|
25
|
+
| 第一步 | 航班查询,按经济舱价格排序并**展示全部**航班(每航班一行,含经济舱/公务舱最低价;不截断) | `search` |
|
|
26
26
|
| 第二步 | 创建订单:可选舱位报价查询 → 验舱验价 → 创建订单(常规) | `create-order` |
|
|
27
27
|
| 第三步 | 支付前校验 + 申请出票 | `pay-issue` |
|
|
28
28
|
| 附加 | 查询订单状态/详情 | `order-status` |
|
|
@@ -48,13 +48,13 @@ python3 {baseDir}/scripts/flight_booking.py search --from-city 杭州 --to-city
|
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
- 支持的城市名示例:杭州、北京、上海、广州、深圳、成都、西安、重庆、南京、武汉、青岛、厦门、昆明、海口、三亚等;也可直接填三字码(如 HGH、BJS、PVG)。若用户只说「杭州到北京」,可直接用城市名执行。
|
|
51
|
-
-
|
|
51
|
+
- **输出**:表格列为「航班号、航空公司、出发机场(含航站楼)、到达机场(含航站楼)、起飞时间、到达时间、经济舱、公务舱、机建费、燃油费」;起飞时间与到达时间为**两列**分别展示,按经济舱价格排序,**返回当日全部航班,不做条数截断**。航站楼信息拼在机场名后(如"萧山机场T3")。机建费和燃油费为每张票的附加税费。
|
|
52
52
|
- **状态保存**:查询成功后会将本次结果写入 `~/.openclaw/flight-booking/.last_search.json`,供下一步创建订单时使用;用户只需选择航班号即可下单,无需再次输入出发/到达/日期。
|
|
53
53
|
- 将表格展示给用户后,由用户选择要预订的**航班号**(及可选舱位:经济舱 Y / 公务舱 C,默认 Y)。
|
|
54
54
|
|
|
55
55
|
**航班查询结果向用户展示时,请务必做到信息完整、条数完整:**
|
|
56
56
|
|
|
57
|
-
-
|
|
57
|
+
- **条数(重点强调)**:脚本返回多少条就展示多少条,**严禁只展示前 15 条 / 前 N 条 / 部分条数**,也不得以"航班较多,仅展示部分"等理由省略。即使返回 30+ 条也必须**逐条全部列出**,由用户自行选择。
|
|
58
58
|
- **每条航班必须包含的字段**:航班号、航空公司、出发机场(含航站楼)、到达机场(含航站楼)、**起飞时间**、**到达时间**、**经济舱价格**、**公务舱价格**、**机建费**、**燃油费**。起飞时间与到达时间为**两列**,缺一不可;若某航班无公务舱则显示「—」或「无」,不得省略该列。
|
|
59
59
|
- **时间**:脚本输出为「起飞时间」「到达时间」两列,向用户展示时也须保留这两列,**不得用 ? 或空白代替到达时间**;若数据中有 `toTime` 必须原样展示。
|
|
60
60
|
- **价格**:经济舱、公务舱两列都要展示;无该舱位时用「—」表示,不得整列不显示。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
|
|
3
|
+
机票查询与下单脚本:支持航班查询(返回全部航班,不截断)、创建订单、支付校验与出票、订单状态查询、取消订单、退票。
|
|
4
4
|
乘机人信息从环境变量读取,用户需提前配置。
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -155,8 +155,9 @@ def _extract_hhmm(time_str: str) -> str:
|
|
|
155
155
|
return s[:5] if len(s) >= 5 else s
|
|
156
156
|
|
|
157
157
|
|
|
158
|
-
def list_flights_simple(result: dict, top_n: int =
|
|
159
|
-
"""
|
|
158
|
+
def list_flights_simple(result: dict, top_n: int | None = None) -> list[dict]:
|
|
159
|
+
"""提取航班列表,每航班一行含经济舱/公务舱最低价、机建燃油费,按经济舱价格排序。
|
|
160
|
+
top_n=None 时返回全部航班(默认),不做条数截断。"""
|
|
160
161
|
if result.get("code") != "0":
|
|
161
162
|
return []
|
|
162
163
|
data = result.get("data", {})
|
|
@@ -192,6 +193,8 @@ def list_flights_simple(result: dict, top_n: int = 15) -> list[dict]:
|
|
|
192
193
|
"fuelTax": f.get("fuelTax", 0),
|
|
193
194
|
})
|
|
194
195
|
out.sort(key=lambda r: (r.get("economyPrice") is None, r.get("economyPrice") or 0))
|
|
196
|
+
if top_n is None:
|
|
197
|
+
return out
|
|
195
198
|
return out[:top_n]
|
|
196
199
|
|
|
197
200
|
|
|
@@ -726,9 +729,10 @@ def cmd_search(args: argparse.Namespace) -> int:
|
|
|
726
729
|
json.dump(state, f, ensure_ascii=False, indent=2)
|
|
727
730
|
except OSError:
|
|
728
731
|
pass # 忽略写入失败,仅影响后续 create-order 需传全参
|
|
729
|
-
rows = list_flights_simple(res, top_n=
|
|
732
|
+
rows = list_flights_simple(res, top_n=None)
|
|
730
733
|
_print_flights_table(rows)
|
|
731
734
|
print()
|
|
735
|
+
print(f"共 {len(rows)} 条航班(已按经济舱价格升序展示全部,未截断)。")
|
|
732
736
|
print("已保存最近一次查询结果。下单请使用: create-order --flight-no <航班号> [--cabin-grade Y|C]")
|
|
733
737
|
return 0
|
|
734
738
|
|
|
@@ -1166,7 +1170,7 @@ def main() -> int:
|
|
|
1166
1170
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
1167
1171
|
|
|
1168
1172
|
# search:支持三字码或城市名(如 杭州、北京)
|
|
1169
|
-
p_search = sub.add_parser("search", help="
|
|
1173
|
+
p_search = sub.add_parser("search", help="航班查询,返回当日全部航班(按经济舱价格排序,不截断)")
|
|
1170
1174
|
p_search.add_argument("--from-city", required=True, help="出发城市:三字码(如 HGH)或城市名(如 杭州)")
|
|
1171
1175
|
p_search.add_argument("--to-city", required=True, help="到达城市:三字码(如 BJS)或城市名(如 北京)")
|
|
1172
1176
|
p_search.add_argument("--from-date", required=True, help="出发日期 YYYY-MM-DD")
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sophnet-video-generate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"types": [
|
|
5
|
-
"builtin"
|
|
5
|
+
"builtin",
|
|
6
|
+
"store"
|
|
6
7
|
],
|
|
7
8
|
"displayName": "",
|
|
8
9
|
"description": "",
|
|
9
10
|
"changelog": [
|
|
11
|
+
{
|
|
12
|
+
"version": "1.0.2",
|
|
13
|
+
"date": "2026-05-11",
|
|
14
|
+
"changes": [
|
|
15
|
+
"补充 store 类型元数据维护,递增版本号"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"version": "1.0.1",
|
|
20
|
+
"date": "2026-05-09",
|
|
21
|
+
"changes": [
|
|
22
|
+
"默认模型调整为 Seedance-2.0",
|
|
23
|
+
"修复默认 Seedance 参数兼容性(使用 resolution + duration)",
|
|
24
|
+
"保留用户指定模型优先策略"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
10
27
|
{
|
|
11
28
|
"version": "1.0.0",
|
|
12
29
|
"date": "2026-04-09",
|
|
@@ -16,5 +33,5 @@
|
|
|
16
33
|
}
|
|
17
34
|
],
|
|
18
35
|
"createdAt": "2026-04-09",
|
|
19
|
-
"updatedAt": "2026-
|
|
36
|
+
"updatedAt": "2026-05-11"
|
|
20
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sophnet-video-generate
|
|
3
|
-
description: "Generate videos from text prompts or images using Sophnet API. Supports text-to-video (T2V) and image-to-video (I2V) generation with models including
|
|
3
|
+
description: "Generate videos from text prompts or images using Sophnet API. Supports text-to-video (T2V) and image-to-video (I2V) generation with models including Seedance-2.0 (default), Seedance-2.0-Fast, happyhorse-1.0-t2v/i2v, Wan2.6-T2V/I2V, ViduQ2-pro, Seedance-1.5-Pro, and any model name documented for the Sophnet video API. Use when users request video generation, ask to create videos from descriptions, convert images to videos, or specify video generation models. "
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Video Generator
|
|
@@ -9,8 +9,10 @@ Generate videos using Sophnet's video generation API with support for both text-
|
|
|
9
9
|
|
|
10
10
|
## Supported Models
|
|
11
11
|
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
12
|
+
- **Seedance-2.0**: Default model (works for both text-to-video and image-to-video)
|
|
13
|
+
- **Seedance-2.0-Fast**: Lower latency Seedance variant
|
|
14
|
+
- **happyhorse-1.0-t2v / happyhorse-1.0-i2v**: Optional HappyHorse models
|
|
15
|
+
- **Wan2.6-T2V / Wan2.6-I2V**: Example alternatives when the user names them explicitly
|
|
14
16
|
- **ViduQ2-pro**: Supports both T2V and I2V
|
|
15
17
|
- **Seedance-1.5-Pro**: Supports both T2V and I2V
|
|
16
18
|
|
|
@@ -34,16 +36,15 @@ Use the absolute path for the `--first-frame` argument.
|
|
|
34
36
|
|
|
35
37
|
### 2. Select Model
|
|
36
38
|
|
|
37
|
-
- If
|
|
38
|
-
- If no
|
|
39
|
-
- If user specifies model → use their choice
|
|
39
|
+
- If user didn't specify model → use Seedance-2.0
|
|
40
|
+
- If user specifies model → pass `--model` through to the API as given (no forced switch to default model). Exception: if they use `happyhorse-1.0-t2v` with `--first-frame`, the script uses `happyhorse-1.0-i2v`; `Wan2.6-T2V` with a first frame is adjusted to `Wan2.6-I2V` when building the request (modality only).
|
|
40
41
|
|
|
41
42
|
### 3. Run Generation Script
|
|
42
43
|
|
|
43
44
|
```bash
|
|
44
45
|
uv run python {baseDir}/scripts/gen_video.py \
|
|
45
46
|
--prompt "Your video description" \
|
|
46
|
-
--model "
|
|
47
|
+
--model "Seedance-2.0" \
|
|
47
48
|
--size "1280*720" \
|
|
48
49
|
--duration 5 \
|
|
49
50
|
--first-frame "/absolute/path/to/image.jpg"
|
|
@@ -66,7 +67,7 @@ The script will:
|
|
|
66
67
|
3. Poll the API until completion
|
|
67
68
|
4. Display the final video URL and local save path
|
|
68
69
|
5. Prompt the user to download the video
|
|
69
|
-
6.
|
|
70
|
+
6. Lists other common models in console output; any documented Sophnet video model name can be passed via `--model`.
|
|
70
71
|
|
|
71
72
|
Example output:
|
|
72
73
|
|
|
@@ -104,7 +105,7 @@ uv run python {baseDir}/scripts/gen_video.py \
|
|
|
104
105
|
```bash
|
|
105
106
|
uv run python {baseDir}/scripts/gen_video.py \
|
|
106
107
|
--prompt "A cat playing with yarn" \
|
|
107
|
-
--model "
|
|
108
|
+
--model "Seedance-2.0"
|
|
108
109
|
```
|
|
109
110
|
|
|
110
111
|
## Notes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
Sophnet Video Generation Script
|
|
4
|
-
Supports text-to-video and image-to-video
|
|
4
|
+
Supports text-to-video and image-to-video; default model is Seedance unless the user specifies another model.
|
|
5
5
|
"""
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
@@ -22,7 +22,7 @@ GENERATE_URL = "https://www.sophnet.com/api/open-apis/projects/easyllms/videogen
|
|
|
22
22
|
|
|
23
23
|
def create_request(
|
|
24
24
|
prompt: Optional[str],
|
|
25
|
-
model: str = "
|
|
25
|
+
model: str = "Seedance-2.0",
|
|
26
26
|
negative_prompt: Optional[str] = None,
|
|
27
27
|
first_frame_url: Optional[str] = None,
|
|
28
28
|
last_frame_url: Optional[str] = None,
|
|
@@ -48,8 +48,10 @@ def create_request(
|
|
|
48
48
|
"image_url": {"url": first_frame_url},
|
|
49
49
|
"role": "first_frame"
|
|
50
50
|
})
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
# Modality fix only: same family T2V + image → I2V (does not replace unrelated user model choices).
|
|
52
|
+
if model == "happyhorse-1.0-t2v":
|
|
53
|
+
model = "happyhorse-1.0-i2v"
|
|
54
|
+
elif model == "Wan2.6-T2V":
|
|
53
55
|
model = "Wan2.6-I2V"
|
|
54
56
|
|
|
55
57
|
# Add last frame
|
|
@@ -59,14 +61,22 @@ def create_request(
|
|
|
59
61
|
"image_url": {"url": last_frame_url},
|
|
60
62
|
"role": "last_frame"
|
|
61
63
|
})
|
|
62
|
-
else:
|
|
63
|
-
model = "Wan2.6-T2V"
|
|
64
64
|
|
|
65
|
-
# Build parameters
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
65
|
+
# Build parameters.
|
|
66
|
+
# Seedance uses resolution levels (e.g. 720p/1080p), while some other models accept size (e.g. 1280*720).
|
|
67
|
+
def infer_resolution_from_size(raw_size: str) -> str:
|
|
68
|
+
normalized = raw_size.lower().replace("*", "x")
|
|
69
|
+
if normalized in {"1920x1080", "1080x1920"}:
|
|
70
|
+
return "1080p"
|
|
71
|
+
if normalized in {"1280x720", "720x1280", "1088x832", "832x1088", "960x960"}:
|
|
72
|
+
return "720p"
|
|
73
|
+
return "720p"
|
|
74
|
+
|
|
75
|
+
parameters = {"duration": duration}
|
|
76
|
+
if model in {"Seedance-2.0", "Seedance-2.0-Fast", "Seedance-1.5-Pro"}:
|
|
77
|
+
parameters["resolution"] = infer_resolution_from_size(size)
|
|
78
|
+
else:
|
|
79
|
+
parameters["size"] = size.replace("x", "*")
|
|
70
80
|
|
|
71
81
|
return {"model": model, "content": content, "parameters": parameters}
|
|
72
82
|
|
|
@@ -170,7 +180,7 @@ def get_file_with_wget(url: str, result_name: str, result_path: str) -> bool:
|
|
|
170
180
|
|
|
171
181
|
def gen_video(
|
|
172
182
|
prompt: str,
|
|
173
|
-
model: str = "
|
|
183
|
+
model: str = "Seedance-2.0",
|
|
174
184
|
size: str = "1280*720",
|
|
175
185
|
duration: int = 5,
|
|
176
186
|
first_frame: Optional[str] = None,
|
|
@@ -246,38 +256,42 @@ def gen_video(
|
|
|
246
256
|
time.sleep(5)
|
|
247
257
|
|
|
248
258
|
if __name__ == "__main__":
|
|
249
|
-
|
|
259
|
+
# Listed for UX hints only; unknown models are still sent to the API as the user passed them.
|
|
260
|
+
supported_models = [
|
|
261
|
+
"happyhorse-1.0-t2v",
|
|
262
|
+
"happyhorse-1.0-i2v",
|
|
263
|
+
"Wan2.6-T2V",
|
|
264
|
+
"Wan2.6-I2V",
|
|
265
|
+
"ViduQ2-pro",
|
|
266
|
+
"Seedance-2.0",
|
|
267
|
+
"Seedance-2.0-Fast",
|
|
268
|
+
"Seedance-1.5-Pro",
|
|
269
|
+
]
|
|
250
270
|
parser = argparse.ArgumentParser(
|
|
251
271
|
description="Generate videos using Sophnet Video Generation API"
|
|
252
272
|
)
|
|
253
273
|
parser.add_argument("--prompt", help="Text prompt (required for text-to-video)")
|
|
254
|
-
parser.add_argument("--model", type=str, default="
|
|
255
|
-
help="Model name. Defaults to
|
|
274
|
+
parser.add_argument("--model", type=str, default="Seedance-2.0",
|
|
275
|
+
help="Model name. Defaults to Seedance-2.0")
|
|
256
276
|
parser.add_argument("--size", type=str, default="1280*720", help="Resolution (default: 1280*720)")
|
|
257
277
|
parser.add_argument("--duration", type=int, default=5, help="Video duration in seconds (default: 5)")
|
|
258
278
|
parser.add_argument("--first-frame", type=str, help="First frame image URL or path")
|
|
259
279
|
parser.add_argument("--generate-audio", action="store_true", help="Generate audio")
|
|
260
280
|
args = parser.parse_args()
|
|
261
281
|
|
|
262
|
-
# Auto-select model based on whether first frame is provided
|
|
263
282
|
model = args.model
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
model = "
|
|
267
|
-
|
|
268
|
-
# If no image provided, ensure using T2V
|
|
269
|
-
model = "Wan2.6-T2V"
|
|
283
|
+
# HappyHorse default: T2V + image → I2V (API expects I2V when a frame is supplied).
|
|
284
|
+
if args.first_frame and model == "happyhorse-1.0-t2v":
|
|
285
|
+
model = "happyhorse-1.0-i2v"
|
|
286
|
+
# Friendly name normalization only (canonical Vidu / Seedance names).
|
|
270
287
|
elif model.find("ViduQ2") != -1:
|
|
271
288
|
model = "ViduQ2-pro"
|
|
272
|
-
elif model.
|
|
273
|
-
model = "Seedance-
|
|
289
|
+
elif model.strip().lower() == "seedance":
|
|
290
|
+
model = "Seedance-2.0"
|
|
291
|
+
elif model.strip().lower() == "seedance-fast":
|
|
292
|
+
model = "Seedance-2.0-Fast"
|
|
274
293
|
elif model not in supported_models:
|
|
275
|
-
|
|
276
|
-
print(f"警告: 模型 '{args.model}' 不受支持,已自动切换到 Wan2.6-I2V")
|
|
277
|
-
model = "Wan2.6-I2V"
|
|
278
|
-
else:
|
|
279
|
-
print(f"警告: 模型 '{args.model}' 不受支持,已自动切换到 Wan2.6-T2V")
|
|
280
|
-
model = "Wan2.6-T2V"
|
|
294
|
+
print(f"提示: 模型 '{args.model}' 不在内置常用列表中,将按您指定的名称原样调用 API。")
|
|
281
295
|
|
|
282
296
|
result_url, local_video_url = gen_video(
|
|
283
297
|
args.prompt,
|
|
@@ -298,7 +312,10 @@ if __name__ == "__main__":
|
|
|
298
312
|
other_models_str = "、".join(other_models)
|
|
299
313
|
print(f"当前使用的模型为 {model},还支持 {other_models_str}")
|
|
300
314
|
print(f"如果想要指定模型,可以直接对我说:使用 [模型名称] 模型,生成 [视频描述]")
|
|
301
|
-
print(
|
|
315
|
+
print(
|
|
316
|
+
"提示: 常用模型示例: happyhorse-1.0-t2v、happyhorse-1.0-i2v、"
|
|
317
|
+
"Wan2.6-T2V、Wan2.6-I2V、ViduQ2-pro、Seedance-2.0、Seedance-2.0-Fast、Seedance-1.5-Pro(也可传入文档中的其他模型名)"
|
|
318
|
+
)
|
|
302
319
|
else:
|
|
303
320
|
print("\n视频生成失败")
|
|
304
321
|
sys.exit(1)
|