|
- package helper
-
- import (
- "fmt"
- "math"
- "strconv"
- "strings"
-
- "github.com/QuantumNous/new-api/common"
- )
-
- func init() {
- registerDerivedPricingFunc("megapixels", derivedMegapixels)
- registerDerivedPricingFunc("aspect_ratio", derivedAspectRatio)
- registerDerivedPricingFunc("input_mode", derivedInputMode)
- registerDerivedPricingFunc("video_input", derivedVideoInput)
- }
-
- func derivedMegapixels(req, resp map[string]any) (any, error) {
- resolution, ok, _ := getNestedValue(req, "resolution")
- if !ok || !hasNonEmptyValue(resolution) {
- resolution, ok, _ = getNestedValue(req, "options.size")
- }
- if !ok || !hasNonEmptyValue(resolution) {
- return nil, nil
- }
- width, height, err := parseResolution(fmt.Sprintf("%v", resolution))
- if err != nil {
- return nil, err
- }
- return float64(width*height) / 1_000_000, nil
- }
-
- func derivedAspectRatio(req, resp map[string]any) (any, error) {
- resolution, ok, _ := getNestedValue(req, "resolution")
- if !ok || !hasNonEmptyValue(resolution) {
- resolution, ok, _ = getNestedValue(req, "options.size")
- }
- if !ok || !hasNonEmptyValue(resolution) {
- return nil, nil
- }
- width, height, err := parseResolution(fmt.Sprintf("%v", resolution))
- if err != nil {
- return nil, err
- }
- if height == 0 {
- return nil, fmt.Errorf("resolution height must be greater than zero")
- }
- gcd := intGCD(width, height)
- return fmt.Sprintf("%d:%d", width/gcd, height/gcd), nil
- }
-
- func derivedInputMode(req, resp map[string]any) (any, error) {
- if hasAnyNonEmpty(req, "video", "video_url", "reference_video", "first_frame_video") {
- return "v2v", nil
- }
- if hasAnyNonEmpty(req, "image", "image_url", "image[]", "images", "first_frame_image", "last_frame_image") {
- return "i2v", nil
- }
- return "t2v", nil
- }
-
- func derivedVideoInput(req, resp map[string]any) (any, error) {
- if hasAnyNonEmpty(req, "video", "video_url", "reference_video", "first_frame_video") {
- return true, nil
- }
- metadata, ok, _ := getNestedValue(req, "metadata")
- if !ok || !hasNonEmptyValue(metadata) {
- return false, nil
- }
- metadataMap, err := normalizeMetadataMap(metadata)
- if err != nil {
- return nil, err
- }
- return metadataContainsVideoInput(metadataMap), nil
- }
-
- func normalizeMetadataMap(raw any) (map[string]any, error) {
- switch value := raw.(type) {
- case map[string]any:
- return value, nil
- case string:
- if strings.TrimSpace(value) == "" {
- return nil, nil
- }
- metadata := map[string]any{}
- if err := common.Unmarshal([]byte(value), &metadata); err != nil {
- return nil, err
- }
- return metadata, nil
- default:
- return nil, nil
- }
- }
-
- func metadataContainsVideoInput(metadata map[string]any) bool {
- if metadata == nil {
- return false
- }
- content, ok := metadata["content"].([]any)
- if !ok {
- return false
- }
- for _, item := range content {
- m, ok := item.(map[string]any)
- if !ok {
- continue
- }
- if typ, _ := m["type"].(string); typ == "video_url" {
- return true
- }
- if value, ok := m["video_url"]; ok && hasNonEmptyValue(value) {
- return true
- }
- }
- return false
- }
-
- func hasNonEmptyValue(v any) bool {
- switch value := v.(type) {
- case nil:
- return false
- case string:
- return strings.TrimSpace(value) != ""
- case []any:
- return len(value) > 0
- case []string:
- return len(value) > 0
- case map[string]any:
- return len(value) > 0
- default:
- return true
- }
- }
-
- func hasAnyNonEmpty(req map[string]any, keys ...string) bool {
- for _, key := range keys {
- value, ok, _ := getNestedValue(req, key)
- if ok && hasNonEmptyValue(value) {
- return true
- }
- }
- return false
- }
-
- func parseResolution(raw string) (int, int, error) {
- normalized := strings.ToLower(strings.TrimSpace(raw))
- if strings.HasSuffix(normalized, "p") {
- height, err := strconv.Atoi(strings.TrimSuffix(normalized, "p"))
- if err != nil || height <= 0 {
- return 0, 0, fmt.Errorf("invalid resolution %q", raw)
- }
- width := int(math.Round(float64(height) * 16 / 9))
- return width, height, nil
- }
-
- parts := strings.FieldsFunc(normalized, func(r rune) bool {
- return r == 'x' || r == '*' || r == '×'
- })
- if len(parts) != 2 {
- return 0, 0, fmt.Errorf("invalid resolution %q", raw)
- }
- width, err := strconv.Atoi(strings.TrimSpace(parts[0]))
- if err != nil || width <= 0 {
- return 0, 0, fmt.Errorf("invalid resolution width %q", raw)
- }
- height, err := strconv.Atoi(strings.TrimSpace(parts[1]))
- if err != nil || height <= 0 {
- return 0, 0, fmt.Errorf("invalid resolution height %q", raw)
- }
- return width, height, nil
- }
-
- func intGCD(a, b int) int {
- for b != 0 {
- a, b = b, a%b
- }
- if a < 0 {
- return -a
- }
- if a == 0 {
- return 1
- }
- return a
- }
|