Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

127 linhas
3.1 KiB

  1. package helper
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. "strings"
  7. )
  8. func init() {
  9. registerDerivedPricingFunc("megapixels", derivedMegapixels)
  10. registerDerivedPricingFunc("aspect_ratio", derivedAspectRatio)
  11. registerDerivedPricingFunc("input_mode", derivedInputMode)
  12. }
  13. func derivedMegapixels(req, resp map[string]any) (any, error) {
  14. resolution, ok, _ := getNestedValue(req, "resolution")
  15. if !ok || !hasNonEmptyValue(resolution) {
  16. resolution, ok, _ = getNestedValue(req, "options.size")
  17. }
  18. if !ok || !hasNonEmptyValue(resolution) {
  19. return nil, nil
  20. }
  21. width, height, err := parseResolution(fmt.Sprintf("%v", resolution))
  22. if err != nil {
  23. return nil, err
  24. }
  25. return float64(width*height) / 1_000_000, nil
  26. }
  27. func derivedAspectRatio(req, resp map[string]any) (any, error) {
  28. resolution, ok, _ := getNestedValue(req, "resolution")
  29. if !ok || !hasNonEmptyValue(resolution) {
  30. resolution, ok, _ = getNestedValue(req, "options.size")
  31. }
  32. if !ok || !hasNonEmptyValue(resolution) {
  33. return nil, nil
  34. }
  35. width, height, err := parseResolution(fmt.Sprintf("%v", resolution))
  36. if err != nil {
  37. return nil, err
  38. }
  39. if height == 0 {
  40. return nil, fmt.Errorf("resolution height must be greater than zero")
  41. }
  42. gcd := intGCD(width, height)
  43. return fmt.Sprintf("%d:%d", width/gcd, height/gcd), nil
  44. }
  45. func derivedInputMode(req, resp map[string]any) (any, error) {
  46. if hasAnyNonEmpty(req, "video", "video_url", "reference_video", "first_frame_video") {
  47. return "v2v", nil
  48. }
  49. if hasAnyNonEmpty(req, "image", "image_url", "image[]", "images", "first_frame_image", "last_frame_image") {
  50. return "i2v", nil
  51. }
  52. return "t2v", nil
  53. }
  54. func hasNonEmptyValue(v any) bool {
  55. switch value := v.(type) {
  56. case nil:
  57. return false
  58. case string:
  59. return strings.TrimSpace(value) != ""
  60. case []any:
  61. return len(value) > 0
  62. case []string:
  63. return len(value) > 0
  64. case map[string]any:
  65. return len(value) > 0
  66. default:
  67. return true
  68. }
  69. }
  70. func hasAnyNonEmpty(req map[string]any, keys ...string) bool {
  71. for _, key := range keys {
  72. value, ok, _ := getNestedValue(req, key)
  73. if ok && hasNonEmptyValue(value) {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func parseResolution(raw string) (int, int, error) {
  80. normalized := strings.ToLower(strings.TrimSpace(raw))
  81. if strings.HasSuffix(normalized, "p") {
  82. height, err := strconv.Atoi(strings.TrimSuffix(normalized, "p"))
  83. if err != nil || height <= 0 {
  84. return 0, 0, fmt.Errorf("invalid resolution %q", raw)
  85. }
  86. width := int(math.Round(float64(height) * 16 / 9))
  87. return width, height, nil
  88. }
  89. parts := strings.FieldsFunc(normalized, func(r rune) bool {
  90. return r == 'x' || r == '*' || r == '×'
  91. })
  92. if len(parts) != 2 {
  93. return 0, 0, fmt.Errorf("invalid resolution %q", raw)
  94. }
  95. width, err := strconv.Atoi(strings.TrimSpace(parts[0]))
  96. if err != nil || width <= 0 {
  97. return 0, 0, fmt.Errorf("invalid resolution width %q", raw)
  98. }
  99. height, err := strconv.Atoi(strings.TrimSpace(parts[1]))
  100. if err != nil || height <= 0 {
  101. return 0, 0, fmt.Errorf("invalid resolution height %q", raw)
  102. }
  103. return width, height, nil
  104. }
  105. func intGCD(a, b int) int {
  106. for b != 0 {
  107. a, b = b, a%b
  108. }
  109. if a < 0 {
  110. return -a
  111. }
  112. if a == 0 {
  113. return 1
  114. }
  115. return a
  116. }