Files
ttstd-infinite-canvas/backend/internal/app/provider_video_options.go

294 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"strconv"
"strings"
"time"
)
func isPublicMediaURL(value string) bool {
lower := strings.ToLower(value)
return strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://")
}
func isSeedanceVideoConfig(config providerConfig) bool {
model := strings.ToLower(config.Model)
return strings.Contains(model, "seedance") || strings.Contains(model, "doubao-seedance") || isArkPlanVideoConfig(config)
}
func isGrokVideoConfig(config providerConfig) bool {
return strings.Contains(strings.ToLower(strings.TrimSpace(config.Model)), "grok")
}
func isArkPlanVideoConfig(config providerConfig) bool {
return strings.Contains(strings.ToLower(config.BaseURL), "/api/plan/v3")
}
func normalizeImageQuality(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case "1k":
return "low"
case "2k":
return "medium"
case "4k":
return "high"
default:
return value
}
}
func imageParameterSupported(profile *ImageCapabilityConfig, parameter string) bool {
if profile == nil {
return true
}
if parameter == "response_format" {
return profile.ResponseFormat.Supported
}
return profile.OutputFormat.Supported
}
func imageQualitySupported(profile *ImageCapabilityConfig) bool {
return profile == nil || profile.Quality.Supported
}
func imageTransparentBackgroundSupported(profile *ImageCapabilityConfig) bool {
return profile == nil || profile.TransparentBackground.Supported
}
func imageSizeParameter(profile *ImageCapabilityConfig, value string) (string, string) {
if profile == nil {
return "size", normalizePixelSize(value)
}
value = strings.TrimSpace(value)
if strings.EqualFold(value, "auto") {
return "", ""
}
if value == "" {
value = strings.TrimSpace(profile.Size.Default)
}
switch profile.Size.Parameter {
case "size":
return "size", normalizePixelSize(value)
case "aspect_ratio":
return "aspect_ratio", normalizeImageAspectRatio(value)
default:
return "", ""
}
}
func normalizeImageAspectRatio(value string) string {
value = strings.TrimSpace(strings.ToLower(strings.ReplaceAll(value, "×", "x")))
if strings.Contains(value, ":") {
return value
}
parts := strings.Split(value, "x")
if len(parts) != 2 {
return ""
}
width, widthErr := strconv.Atoi(parts[0])
height, heightErr := strconv.Atoi(parts[1])
if widthErr != nil || heightErr != nil || width <= 0 || height <= 0 {
return ""
}
divisor := imageDimensionGCD(width, height)
return strconv.Itoa(width/divisor) + ":" + strconv.Itoa(height/divisor)
}
func imageDimensionGCD(left int, right int) int {
for right != 0 {
left, right = right, left%right
}
if left < 1 {
return 1
}
return left
}
func normalizePixelSize(value string) string {
value = strings.TrimSpace(value)
if value == "" || value == "auto" {
return ""
}
// 画布按比例保存常用预设;图片接口只接受像素尺寸,必须在请求边界完成转换。
switch value {
case "1:1":
return "1024x1024"
case "3:2":
return "1536x1024"
case "2:3":
return "1024x1536"
case "4:3":
return "1360x1024"
case "3:4":
return "1024x1360"
case "16:9":
return "1824x1024"
case "9:16":
return "1024x1824"
case "21:9":
return "2352x1008"
}
if strings.Contains(value, "x") {
return value
}
return ""
}
func normalizeVideoSize(value string) string {
value = strings.TrimSpace(value)
if value == "" || value == "auto" {
return ""
}
if strings.Contains(value, "x") {
return value
}
if value == "9:16" || value == "2:3" || value == "3:4" {
return "720x1280"
}
return "1280x720"
}
func normalizeVideoResolution(value string) string {
value = strings.TrimSpace(value)
if value == "" || value == "auto" || value == "medium" || value == "high" {
return "720p"
}
if value == "low" {
return "480p"
}
if strings.EqualFold(value, "4k") {
return "2160p"
}
if strings.EqualFold(value, "2k") {
return "1440p"
}
if strings.HasSuffix(value, "p") {
return value
}
return value + "p"
}
func videoResolutionNameRequest(profile *VideoCapabilityConfig, value string) string {
requested := strings.ToLower(strings.TrimSpace(value))
if requested == "" || requested == "auto" || requested == "default" || requested == "medium" || requested == "high" {
return ""
}
if profile == nil {
return ""
}
if len(profile.Resolutions) == 0 {
return ""
}
candidates := []string{requested, strings.ToLower(normalizeVideoResolution(requested))}
if requested == "4k" {
candidates = append(candidates, "2160p")
}
if requested == "2160" || requested == "2160p" {
candidates = append(candidates, "4k")
}
for _, supported := range profile.Resolutions {
for _, candidate := range candidates {
if strings.EqualFold(strings.TrimSpace(supported), candidate) {
return strings.TrimSpace(supported)
}
}
}
return ""
}
func isAutomaticVideoResolution(value string) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case "", "auto", "default", "medium", "high":
return true
default:
return false
}
}
func normalizeSeedanceDuration(value string) int {
if strings.TrimSpace(value) == "-1" {
return -1
}
seconds, err := strconv.Atoi(strings.TrimSpace(value))
if err != nil || seconds <= 0 {
return 5
}
return seconds
}
func normalizeSeedanceVideosDuration(value string) int {
return normalizeSeedanceDuration(value)
}
func normalizeSeedanceRatio(value string) string {
value = strings.TrimSpace(value)
if value == "" || value == "auto" || value == "adaptive" {
return "adaptive"
}
switch value {
case "16:9", "9:16", "1:1", "4:3", "3:4", "21:9":
return value
default:
return "adaptive"
}
}
func normalizeSeedanceVideosRatio(value string) string {
ratio := normalizeSeedanceRatio(value)
if ratio == "adaptive" {
return "16:9"
}
return ratio
}
func normalizeSeedanceResolution(value string, model string) string {
resolution := strings.TrimSuffix(strings.TrimSpace(value), "p")
if strings.EqualFold(resolution, "4k") {
resolution = "2160"
}
switch resolution {
case "480", "720", "1080", "2160":
default:
if value == "low" {
resolution = "480"
} else {
resolution = "720"
}
}
if strings.Contains(strings.ToLower(model), "fast") && (resolution == "1080" || resolution == "2160") {
resolution = "720"
}
return resolution + "p"
}
func parseBool(value string, fallback bool) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case "true":
return true
case "false":
return false
default:
return fallback
}
}
func parseFloat(value string, fallback float64) float64 {
number, err := strconv.ParseFloat(strings.TrimSpace(value), 64)
if err != nil || number == 0 {
return fallback
}
return number
}
func sleepContext(ctx context.Context, duration time.Duration) error {
timer := time.NewTimer(duration)
defer timer.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return nil
}
}