fix(media): 规范比例与 OpenAI 图像尺寸参数
统一过滤非 x:x 比例,避免默认值参与候选匹配或透传上游。 支持 xK 分辨率归一化,并按 Gemini、Volces 和 OpenAI 的参数规范生成上游请求;OpenAI 显式 WxH 始终保留用户原始尺寸。 验证:在独立临时 worktree 中执行 apps/api 全量 go test ./... -count=1 通过,真实 OpenAI 请求已到达上游但受本地无效凭据阻断。
This commit is contained in:
@@ -393,23 +393,39 @@ func numberPair(value any) ([2]float64, bool) {
|
||||
}
|
||||
|
||||
func validAspectRatio(value string) bool {
|
||||
if value == "adaptive" || value == "keep_ratio" {
|
||||
return true
|
||||
}
|
||||
_, ok := aspectRatioNumber(value)
|
||||
_, ok := normalizedAspectRatio(value)
|
||||
return ok
|
||||
}
|
||||
|
||||
func aspectRatioNumber(value string) (float64, bool) {
|
||||
parts := strings.Split(value, ":")
|
||||
func normalizedAspectRatio(value string) (string, bool) {
|
||||
compact := strings.Map(func(r rune) rune {
|
||||
switch r {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
return -1
|
||||
default:
|
||||
return r
|
||||
}
|
||||
}, strings.TrimSpace(value))
|
||||
parts := strings.Split(compact, ":")
|
||||
if len(parts) != 2 {
|
||||
return 0, false
|
||||
return "", false
|
||||
}
|
||||
width := parsePositiveFloat(parts[0])
|
||||
height := parsePositiveFloat(parts[1])
|
||||
if width <= 0 || height <= 0 {
|
||||
return "", false
|
||||
}
|
||||
return parts[0] + ":" + parts[1], true
|
||||
}
|
||||
|
||||
func aspectRatioNumber(value string) (float64, bool) {
|
||||
normalized, ok := normalizedAspectRatio(value)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
parts := strings.Split(normalized, ":")
|
||||
width := parsePositiveFloat(parts[0])
|
||||
height := parsePositiveFloat(parts[1])
|
||||
return width / height, true
|
||||
}
|
||||
|
||||
@@ -537,11 +553,53 @@ func isEmptyParamString(value string) bool {
|
||||
}
|
||||
|
||||
func isImageResolution(modelType string, value string) bool {
|
||||
return (modelType == "image_generate" || modelType == "image_edit") && containsString([]string{"1K", "2K", "3K", "4K", "8K"}, value)
|
||||
if modelType != "image_generate" && modelType != "image_edit" {
|
||||
return false
|
||||
}
|
||||
return normalizeImageResolution(value) != ""
|
||||
}
|
||||
|
||||
func isVideoResolution(modelType string, value string) bool {
|
||||
return isVideoModelType(modelType) && containsString([]string{"480p", "720p", "1080p", "1440p", "2160p"}, value)
|
||||
return isVideoModelType(modelType) && normalizeVideoResolution(value) != ""
|
||||
}
|
||||
|
||||
func normalizeMediaResolution(modelType string, value string) string {
|
||||
if modelType == "image_generate" || modelType == "image_edit" {
|
||||
return normalizeImageResolution(value)
|
||||
}
|
||||
if isVideoModelType(modelType) {
|
||||
return normalizeVideoResolution(value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func normalizeImageResolution(value string) string {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
if len(value) < 2 || value[len(value)-1] != 'k' {
|
||||
return ""
|
||||
}
|
||||
magnitude, err := strconv.Atoi(value[:len(value)-1])
|
||||
if err != nil || magnitude <= 0 {
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(magnitude) + "K"
|
||||
}
|
||||
|
||||
func normalizeVideoResolution(value string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "480p":
|
||||
return "480p"
|
||||
case "720p":
|
||||
return "720p"
|
||||
case "1080p":
|
||||
return "1080p"
|
||||
case "1440p":
|
||||
return "1440p"
|
||||
case "2160p", "4k":
|
||||
return "2160p"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func isVideoModelType(modelType string) bool {
|
||||
|
||||
Reference in New Issue
Block a user