@pippit-dev/cli 1.0.25 → 1.0.26
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/README.md +1 -1
- package/checksums.txt +6 -6
- package/cmd/generate_video/generate_video.go +3 -3
- package/cmd/generate_video_test.go +57 -55
- package/dist/checksums.txt +6 -6
- package/internal/generate_video/generate_video.go +0 -15
- package/package.json +1 -1
- package/skills/xyq-nest-skill/commands/generate-video.md +3 -3
package/README.md
CHANGED
|
@@ -227,7 +227,7 @@ pippit-tool-cli generate-video \
|
|
|
227
227
|
--resolution "720p"
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
-
命令输出 `thread_id`、`run_id` 和 `web_thread_link`。提交生视频 HTTP 请求时,参考图、参考视频和参考音频会使用上传接口返回的 `pippit_asset_id`,并分别写入 `video_part_tool_param.images`、`video_part_tool_param.videos` 和 `video_part_tool_param.audios
|
|
230
|
+
命令输出 `thread_id`、`run_id` 和 `web_thread_link`。提交生视频 HTTP 请求时,参考图、参考视频和参考音频会使用上传接口返回的 `pippit_asset_id`,并分别写入 `video_part_tool_param.images`、`video_part_tool_param.videos` 和 `video_part_tool_param.audios`。图片支持 `.jpg`、`.jpeg`、`.png`、`.gif`、`.bmp`、`.webp`、`.svg`;视频支持 `.mp4`、`.avi`、`.mov`、`.wmv`、`.flv`、`.webm`、`.mkv`、`.m4v`;音频仅支持 `.mp3`、`.wav`。普通用户支持模型 `Seedance_2.0_mini_lite`;`seedance2.0_vision`、`seedance2.0_fast_vision`、`Seedance_2.0_mini` 和 `Seedance_2.5` 为 VIP 专属模型。CLI 会在提交前校验 prompt 和文件后缀;模型、比例、分辨率等语义校验由服务端处理。
|
|
231
231
|
|
|
232
232
|
首尾帧生视频时,按首帧、尾帧的顺序传入两次 `--image`,并设置 `--generate-type 1`:
|
|
233
233
|
|
package/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
485d9e51092beaf675a872f890fd744460e7afe45b27ec500ab6b5c90ebb6d0c pippit-tool-cli-1.0.26-darwin-amd64.tar.gz
|
|
2
|
+
978cca38261d539ea8ce2bcfd15deb24b4b317f38c084fdde92a5b9ca51f0739 pippit-tool-cli-1.0.26-darwin-arm64.tar.gz
|
|
3
|
+
d772c3fcef2c7b7ff0e6e52d6f10b2ee4cff1cd5fbdff9f08e0bd252ac378e79 pippit-tool-cli-1.0.26-linux-amd64.tar.gz
|
|
4
|
+
b16e7f2e7d9bbfe122eb0895ae6769a00d230ca250717aebb3f9b8eea0c04eae pippit-tool-cli-1.0.26-linux-arm64.tar.gz
|
|
5
|
+
fd05b98a6001602efad98731a4777cb035eea2bea2acbccb2c1d02af0223176e pippit-tool-cli-1.0.26-windows-amd64.zip
|
|
6
|
+
a0c873456cf254b0e452c6e376efec5a425ef044b3a1a671d063fa5a613242d0 pippit-tool-cli-1.0.26-windows-arm64.zip
|
|
@@ -43,9 +43,9 @@ func NewCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Command
|
|
|
43
43
|
cmd.SetErr(stderr)
|
|
44
44
|
flags := cmd.Flags()
|
|
45
45
|
flags.StringVar(&opts.Prompt, "prompt", "", "video generation prompt")
|
|
46
|
-
flags.StringArrayVar(&opts.ImagePaths, "image", nil, "local reference image path; repeat for multiple images
|
|
47
|
-
flags.StringArrayVar(&opts.VideoPaths, "video", nil, "local reference video path; repeat for multiple videos
|
|
48
|
-
flags.StringArrayVar(&opts.AudioPaths, "audio", nil, "local reference audio path; repeat for multiple audios
|
|
46
|
+
flags.StringArrayVar(&opts.ImagePaths, "image", nil, "local reference image path; repeat for multiple images")
|
|
47
|
+
flags.StringArrayVar(&opts.VideoPaths, "video", nil, "local reference video path; repeat for multiple videos")
|
|
48
|
+
flags.StringArrayVar(&opts.AudioPaths, "audio", nil, "local reference audio path; repeat for multiple audios")
|
|
49
49
|
flags.IntVar(&durationSec, "duration", 0, "video duration in seconds")
|
|
50
50
|
flags.StringVar(&opts.Ratio, "ratio", "", "video ratio, such as 9:16, 16:9, 3:4, 4:3")
|
|
51
51
|
flags.StringVar(&opts.Model, "model", "", "video model; normal users: Seedance_2.0_mini_lite; VIP-only: seedance2.0_vision, seedance2.0_fast_vision, Seedance_2.0_mini, Seedance_2.5")
|
|
@@ -189,72 +189,74 @@ func TestGenerateVideoRequiresPrompt(t *testing.T) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
func
|
|
193
|
-
|
|
194
|
-
t.Fatal("server should not receive request when image count is invalid")
|
|
195
|
-
}))
|
|
196
|
-
defer server.Close()
|
|
197
|
-
|
|
198
|
-
var stdout, stderr bytes.Buffer
|
|
199
|
-
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
200
|
-
args := []string{"generate-video", "--prompt", "x"}
|
|
201
|
-
for _, path := range mediaPaths("image", ".jpg", 10) {
|
|
202
|
-
args = append(args, "--image", path)
|
|
203
|
-
}
|
|
204
|
-
root.SetArgs(args)
|
|
205
|
-
|
|
206
|
-
err := root.Execute()
|
|
207
|
-
if err == nil {
|
|
208
|
-
t.Fatal("Execute() error = nil, want image count validation")
|
|
209
|
-
}
|
|
210
|
-
if !strings.Contains(err.Error(), "参考图片最多支持 9 个,当前传入 10 个") {
|
|
211
|
-
t.Fatalf("error = %q, want image count validation", err)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
func TestGenerateVideoRejectsTooManyVideos(t *testing.T) {
|
|
216
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
217
|
-
t.Fatal("server should not receive request when video count is invalid")
|
|
218
|
-
}))
|
|
219
|
-
defer server.Close()
|
|
220
|
-
|
|
221
|
-
var stdout, stderr bytes.Buffer
|
|
222
|
-
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
192
|
+
func TestGenerateVideoAcceptsReferencesBeyondFormerLimits(t *testing.T) {
|
|
193
|
+
cwd := chdirTemp(t)
|
|
223
194
|
args := []string{"generate-video", "--prompt", "x"}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
195
|
+
var assetIDs []string
|
|
196
|
+
for _, media := range []struct {
|
|
197
|
+
flag string
|
|
198
|
+
ext string
|
|
199
|
+
count int
|
|
200
|
+
}{
|
|
201
|
+
{"image", ".jpg", 10},
|
|
202
|
+
{"video", ".mp4", 4},
|
|
203
|
+
{"audio", ".mp3", 4},
|
|
204
|
+
} {
|
|
205
|
+
for _, name := range mediaPaths(media.flag, media.ext, media.count) {
|
|
206
|
+
path := filepath.Join(cwd, name)
|
|
207
|
+
if err := os.WriteFile(path, []byte("media-data"), 0o644); err != nil {
|
|
208
|
+
t.Fatalf("WriteFile(%s): %v", path, err)
|
|
209
|
+
}
|
|
210
|
+
args = append(args, "--"+media.flag, path)
|
|
211
|
+
assetIDs = append(assetIDs, name)
|
|
212
|
+
}
|
|
235
213
|
}
|
|
236
|
-
}
|
|
237
214
|
|
|
238
|
-
|
|
215
|
+
uploadIndex := 0
|
|
216
|
+
submitted := false
|
|
239
217
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
240
|
-
|
|
218
|
+
switch r.URL.Path {
|
|
219
|
+
case "/api/biz/v1/skill/upload_file":
|
|
220
|
+
if uploadIndex >= len(assetIDs) {
|
|
221
|
+
t.Fatalf("unexpected upload %d", uploadIndex)
|
|
222
|
+
}
|
|
223
|
+
_, _ = w.Write([]byte(`{"ret":"0","data":{"pippit_asset_id":"` + assetIDs[uploadIndex] + `"}}`))
|
|
224
|
+
uploadIndex++
|
|
225
|
+
case "/api/biz/v1/skill/submit_run":
|
|
226
|
+
if uploadIndex != len(assetIDs) {
|
|
227
|
+
t.Fatalf("uploaded %d references, want %d", uploadIndex, len(assetIDs))
|
|
228
|
+
}
|
|
229
|
+
data, err := io.ReadAll(r.Body)
|
|
230
|
+
if err != nil {
|
|
231
|
+
t.Fatalf("read body: %v", err)
|
|
232
|
+
}
|
|
233
|
+
var body map[string]any
|
|
234
|
+
if err := sonic.Unmarshal(data, &body); err != nil {
|
|
235
|
+
t.Fatalf("decode body: %v", err)
|
|
236
|
+
}
|
|
237
|
+
param, ok := body["video_part_tool_param"].(map[string]any)
|
|
238
|
+
if !ok {
|
|
239
|
+
t.Fatalf("video_part_tool_param = %#v, want object", body["video_part_tool_param"])
|
|
240
|
+
}
|
|
241
|
+
assertAssetRefs(t, param["images"], assetIDs[:10])
|
|
242
|
+
assertAssetRefs(t, param["videos"], assetIDs[10:14])
|
|
243
|
+
assertAssetRefs(t, param["audios"], assetIDs[14:])
|
|
244
|
+
submitted = true
|
|
245
|
+
_, _ = w.Write([]byte(`{"ret":"0","data":{"run":{"thread_id":"thread_123","run_id":"run_456"}}}`))
|
|
246
|
+
default:
|
|
247
|
+
t.Fatalf("unexpected path %s", r.URL.Path)
|
|
248
|
+
}
|
|
241
249
|
}))
|
|
242
250
|
defer server.Close()
|
|
243
251
|
|
|
244
252
|
var stdout, stderr bytes.Buffer
|
|
245
253
|
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
246
|
-
args := []string{"generate-video", "--prompt", "x"}
|
|
247
|
-
for _, path := range mediaPaths("audio", ".mp3", 4) {
|
|
248
|
-
args = append(args, "--audio", path)
|
|
249
|
-
}
|
|
250
254
|
root.SetArgs(args)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if err == nil {
|
|
254
|
-
t.Fatal("Execute() error = nil, want audio count validation")
|
|
255
|
+
if err := root.Execute(); err != nil {
|
|
256
|
+
t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
|
|
255
257
|
}
|
|
256
|
-
if !
|
|
257
|
-
t.
|
|
258
|
+
if !submitted {
|
|
259
|
+
t.Fatal("generate-video did not submit references")
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
|
package/dist/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
485d9e51092beaf675a872f890fd744460e7afe45b27ec500ab6b5c90ebb6d0c pippit-tool-cli-1.0.26-darwin-amd64.tar.gz
|
|
2
|
+
978cca38261d539ea8ce2bcfd15deb24b4b317f38c084fdde92a5b9ca51f0739 pippit-tool-cli-1.0.26-darwin-arm64.tar.gz
|
|
3
|
+
d772c3fcef2c7b7ff0e6e52d6f10b2ee4cff1cd5fbdff9f08e0bd252ac378e79 pippit-tool-cli-1.0.26-linux-amd64.tar.gz
|
|
4
|
+
b16e7f2e7d9bbfe122eb0895ae6769a00d230ca250717aebb3f9b8eea0c04eae pippit-tool-cli-1.0.26-linux-arm64.tar.gz
|
|
5
|
+
fd05b98a6001602efad98731a4777cb035eea2bea2acbccb2c1d02af0223176e pippit-tool-cli-1.0.26-windows-amd64.zip
|
|
6
|
+
a0c873456cf254b0e452c6e376efec5a425ef044b3a1a671d063fa5a613242d0 pippit-tool-cli-1.0.26-windows-arm64.zip
|
|
@@ -9,12 +9,6 @@ import (
|
|
|
9
9
|
"github.com/Pippit-dev/pippit-cli/internal/common"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
const (
|
|
13
|
-
maxReferenceImages = 9
|
|
14
|
-
maxReferenceVideos = 3
|
|
15
|
-
maxReferenceAudios = 3
|
|
16
|
-
)
|
|
17
|
-
|
|
18
12
|
var (
|
|
19
13
|
allowedImageExtensionList = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"}
|
|
20
14
|
allowedAudioExtensionList = []string{".mp3", ".wav"}
|
|
@@ -70,15 +64,6 @@ func ValidateOptions(opts *Options) error {
|
|
|
70
64
|
if strings.TrimSpace(opts.Prompt) == "" {
|
|
71
65
|
return fmt.Errorf("缺少必填参数 --prompt")
|
|
72
66
|
}
|
|
73
|
-
if len(opts.ImagePaths) > maxReferenceImages {
|
|
74
|
-
return fmt.Errorf("参考图片最多支持 %d 个,当前传入 %d 个", maxReferenceImages, len(opts.ImagePaths))
|
|
75
|
-
}
|
|
76
|
-
if len(opts.VideoPaths) > maxReferenceVideos {
|
|
77
|
-
return fmt.Errorf("参考视频最多支持 %d 个,当前传入 %d 个", maxReferenceVideos, len(opts.VideoPaths))
|
|
78
|
-
}
|
|
79
|
-
if len(opts.AudioPaths) > maxReferenceAudios {
|
|
80
|
-
return fmt.Errorf("参考音频最多支持 %d 个,当前传入 %d 个", maxReferenceAudios, len(opts.AudioPaths))
|
|
81
|
-
}
|
|
82
67
|
if err := validateMediaExtensions("图片", opts.ImagePaths, allowedImageExtensions, allowedImageExtensionList); err != nil {
|
|
83
68
|
return err
|
|
84
69
|
}
|
package/package.json
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| `--prompt` | 是 | 用户原始描述,不能全为空白 |
|
|
10
10
|
| `--model` | 否 | 用户指定的模型;未提供时省略,由服务端处理默认配置 |
|
|
11
|
-
| `--image` | 否 |
|
|
12
|
-
| `--video` | 否 |
|
|
13
|
-
| `--audio` | 否 | 本地 `.mp3/.wav`
|
|
11
|
+
| `--image` | 否 | 本地图片路径,重复参数 |
|
|
12
|
+
| `--video` | 否 | 本地参考视频路径,重复参数 |
|
|
13
|
+
| `--audio` | 否 | 本地 `.mp3/.wav` 音频路径,重复参数 |
|
|
14
14
|
| `--duration` | 否 | 整数秒;用户只给时长范围时先确认具体秒数 |
|
|
15
15
|
| `--ratio` | 否 | 比例字符串,如 `9:16`、`16:9`、`3:4`、`4:3`;不转换为生图枚举 |
|
|
16
16
|
| `--resolution` | 否 | 用户指定值,如 `720p`、`1080p` |
|