feat: veo3.1 拆分 fast/standard, seedance双模型, per_second定价, 子号过滤, 随机种子, UI优化
This commit is contained in:
@@ -37,6 +37,7 @@ var (
|
||||
ErrQuotaExhausted = errors.New("adobe quota exhausted")
|
||||
ErrTemporaryUpstream = errors.New("adobe upstream temporary error")
|
||||
ErrDeadUpstream = errors.New("adobe upstream fatal error")
|
||||
ErrRateLimited = errors.New("adobe rate limited")
|
||||
// ErrContentRejected is Adobe's content-safety filter refusing the prompt or
|
||||
// the generated image (HTTP 451 image_unsafe). It is the prompt's fault, not
|
||||
// the account's — every account rejects the same content — so the caller must
|
||||
@@ -178,7 +179,10 @@ func (c *Client) uploadImageOnce(ctx context.Context, token string, content []by
|
||||
if resp.StatusCode == 401 || resp.StatusCode == 403 {
|
||||
return nil, fmt.Errorf("%w (upload %d %s: %s)", ErrAuth, resp.StatusCode, resp.Header.Get("x-access-error"), clip(body, 300)), false
|
||||
}
|
||||
if resp.StatusCode == 429 || resp.StatusCode == 451 || resp.StatusCode >= 500 {
|
||||
if resp.StatusCode == 429 {
|
||||
return nil, fmt.Errorf("%w (upload %d): %s", ErrRateLimited, resp.StatusCode, clip(body, 300)), false
|
||||
}
|
||||
if resp.StatusCode == 451 || resp.StatusCode >= 500 {
|
||||
return nil, fmt.Errorf("adobe upload failed: %d %s", resp.StatusCode, clip(body, 300)), true
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
|
||||
@@ -2,8 +2,8 @@ package adobe
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type modelSpec struct {
|
||||
@@ -68,7 +68,7 @@ func ResolveModelSpec(modelID string) modelSpec {
|
||||
func buildImage5Payload(prompt, aspectRatio, resolution string, blobIDs []string) map[string]any {
|
||||
p := map[string]any{
|
||||
"n": 1,
|
||||
"seeds": []int{int(time.Now().Unix()) % 999999},
|
||||
"seeds": []int{rand.Intn(999999)},
|
||||
"output": map[string]any{"storeInputs": true},
|
||||
"prompt": prompt,
|
||||
"referenceBlobs": []any{},
|
||||
@@ -125,7 +125,7 @@ func buildGPTImagePayloads(spec modelSpec, prompt, ratio, resolution string, blo
|
||||
"modelVersion": spec.UpstreamModelVersion,
|
||||
"n": 1,
|
||||
"prompt": prompt,
|
||||
"seeds": []int{int(time.Now().Unix()) % 999999},
|
||||
"seeds": []int{rand.Intn(999999)},
|
||||
"output": map[string]any{"storeInputs": true},
|
||||
"referenceBlobs": []any{},
|
||||
"generationMetadata": map[string]any{"module": "text2image", "submodule": "ff-image-generate"},
|
||||
@@ -151,7 +151,7 @@ func buildFluxPayloads(spec modelSpec, prompt, ratio string, blobIDs []string) [
|
||||
"n": 1,
|
||||
"prompt": prompt,
|
||||
"size": map[string]any{"width": size[0], "height": size[1]},
|
||||
"seeds": []int{int(time.Now().Unix()) % 999999},
|
||||
"seeds": []int{rand.Intn(999999)},
|
||||
"output": map[string]any{"storeInputs": true},
|
||||
"referenceBlobs": []any{},
|
||||
"modelSpecificPayload": map[string]any{
|
||||
@@ -182,7 +182,7 @@ func buildDefaultPayloads(spec modelSpec, prompt, ratio, resolution string, blob
|
||||
"n": 1,
|
||||
"prompt": prompt,
|
||||
"size": map[string]any{"width": size[0], "height": size[1]},
|
||||
"seeds": []int{int(time.Now().Unix()) % 999999},
|
||||
"seeds": []int{rand.Intn(999999)},
|
||||
"groundSearch": false,
|
||||
"output": map[string]any{"storeInputs": true},
|
||||
"generationMetadata": map[string]any{
|
||||
@@ -252,7 +252,7 @@ func cloneMap(in map[string]any) map[string]any {
|
||||
}
|
||||
|
||||
func BuildVideoPayload(engine, prompt, aspectRatio string, durationSeconds int, resolution, referenceMode, upstreamModel string, blobIDs []string) map[string]any {
|
||||
seedVal := int(time.Now().Unix()) % 999999
|
||||
seedVal := rand.Intn(999999)
|
||||
engine = defaultString(engine, "sora2")
|
||||
resolution = defaultString(resolution, "720p")
|
||||
aspectRatio = defaultString(aspectRatio, "16:9")
|
||||
@@ -299,9 +299,6 @@ func BuildVideoPayload(engine, prompt, aspectRatio string, durationSeconds int,
|
||||
if engine == "veo31-standard" {
|
||||
modelVersion = "3.1-generate"
|
||||
}
|
||||
// Shape mirrors a captured working firefly.adobe.com video request: flat
|
||||
// top-level duration / negativePrompt / generateAudio, submodule set, and
|
||||
// NO `n` / NO modelSpecificPayload (sending those got 403).
|
||||
payload := map[string]any{
|
||||
"modelId": "veo",
|
||||
"modelVersion": modelVersion,
|
||||
@@ -310,7 +307,7 @@ func BuildVideoPayload(engine, prompt, aspectRatio string, durationSeconds int,
|
||||
"prompt": prompt,
|
||||
"negativePrompt": "",
|
||||
"duration": durationSeconds,
|
||||
"generateAudio": false,
|
||||
"generateAudio": true,
|
||||
"generationMetadata": map[string]any{
|
||||
"module": "text2video",
|
||||
"submodule": "ff-video-generate",
|
||||
@@ -320,9 +317,49 @@ func BuildVideoPayload(engine, prompt, aspectRatio string, durationSeconds int,
|
||||
}
|
||||
if len(blobIDs) > 0 {
|
||||
payload["generationMetadata"] = map[string]any{"module": "image2video", "submodule": "ff-video-generate"}
|
||||
refs := make([]any, 0, min(len(blobIDs), 2))
|
||||
for idx, id := range blobIDs[:min(len(blobIDs), 2)] {
|
||||
refs = append(refs, map[string]any{"id": id, "usage": "general", "promptReference": idx + 1})
|
||||
refs := make([]any, 0, min(len(blobIDs), 3))
|
||||
if referenceMode == "frame" {
|
||||
for idx, id := range blobIDs[:min(len(blobIDs), 3)] {
|
||||
refs = append(refs, map[string]any{"id": id, "usage": "frame", "order": idx + 1})
|
||||
}
|
||||
} else {
|
||||
for _, id := range blobIDs[:min(len(blobIDs), 3)] {
|
||||
refs = append(refs, map[string]any{"id": id, "usage": "asset"})
|
||||
}
|
||||
}
|
||||
payload["referenceBlobs"] = refs
|
||||
}
|
||||
return payload
|
||||
case "seedance-fast", "seedance-2.0":
|
||||
modelVersion := "seedance_2.0"
|
||||
if engine == "seedance-fast" {
|
||||
modelVersion = "seedance_2.0_fast"
|
||||
}
|
||||
payload := map[string]any{
|
||||
"modelId": "seedance",
|
||||
"modelVersion": modelVersion,
|
||||
"size": videoSize(aspectRatio, resolution),
|
||||
"seeds": []int{seedVal},
|
||||
"prompt": prompt,
|
||||
"negativePrompt": "",
|
||||
"duration": durationSeconds,
|
||||
"generateAudio": true,
|
||||
"generationSettings": map[string]any{"aspectRatio": aspectRatio},
|
||||
"generationMetadata": map[string]any{"module": "text2video", "submodule": "ff-video-generate"},
|
||||
"output": map[string]any{"storeInputs": true},
|
||||
"referenceBlobs": []any{},
|
||||
}
|
||||
if len(blobIDs) > 0 {
|
||||
payload["generationMetadata"] = map[string]any{"module": "image2video", "submodule": "ff-video-generate"}
|
||||
refs := make([]any, 0, min(len(blobIDs), 9))
|
||||
if referenceMode == "frame" {
|
||||
for idx, id := range blobIDs[:min(len(blobIDs), 9)] {
|
||||
refs = append(refs, map[string]any{"id": id, "usage": "frame", "order": idx + 1})
|
||||
}
|
||||
} else {
|
||||
for _, id := range blobIDs[:min(len(blobIDs), 9)] {
|
||||
refs = append(refs, map[string]any{"id": id, "usage": "asset"})
|
||||
}
|
||||
}
|
||||
payload["referenceBlobs"] = refs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user