fix: prefer generated video metadata for billing
This commit is contained in:
@@ -82,19 +82,23 @@ func (s *Service) billings(ctx context.Context, user *auth.User, kind string, bo
|
||||
resource = "video"
|
||||
unit = "5s_video"
|
||||
baseKey = "videoBase"
|
||||
duration := requestDurationSeconds(body)
|
||||
duration, durationSource := billingDurationSeconds(body, response)
|
||||
audioEnabled, audioSource := billingAudioEnabled(body, response)
|
||||
durationUnits := math.Max(1, math.Ceil(duration/5))
|
||||
amount := float64(count) *
|
||||
durationUnits *
|
||||
resourcePrice(config, resource, baseKey, "basePrice") *
|
||||
resourceWeight(config, resource, "resolutionWeights", firstNonEmptyString(stringFromMap(body, "resolution"), stringFromMap(body, "size"))) *
|
||||
resourceWeight(config, resource, "audioWeights", boolWeightKey(boolishValue(body["audio"]))) *
|
||||
resourceWeight(config, resource, "audioWeights", boolWeightKey(audioEnabled)) *
|
||||
resourceWeight(config, resource, "referenceVideoWeights", boolWeightKey(requestHasReferenceVideo(body))) *
|
||||
resourceWeight(config, resource, "voiceSpecifiedWeights", boolWeightKey(requestHasVoiceID(body))) *
|
||||
resourceWeight(config, resource, "voiceSpecifiedWeights", boolWeightKey(requestHasVoiceID(body, audioEnabled))) *
|
||||
discount
|
||||
return []any{billingLineWithDetails(candidate, resource, unit, count*int(durationUnits), roundPrice(amount), discount, simulated, map[string]any{
|
||||
"count": count,
|
||||
"audio": audioEnabled,
|
||||
"audioSource": audioSource,
|
||||
"durationSeconds": duration,
|
||||
"durationSource": durationSource,
|
||||
"durationUnit": "5s",
|
||||
"durationUnitCount": durationUnits,
|
||||
})}
|
||||
@@ -345,6 +349,54 @@ func requestDurationSeconds(body map[string]any) float64 {
|
||||
return 5
|
||||
}
|
||||
|
||||
func billingDurationSeconds(body map[string]any, response clients.Response) (float64, string) {
|
||||
if duration, ok := generatedVideoDurationSeconds(response.Result); ok {
|
||||
return duration, "generated_video"
|
||||
}
|
||||
return requestDurationSeconds(body), "preprocessed_request"
|
||||
}
|
||||
|
||||
func generatedVideoDurationSeconds(result map[string]any) (float64, bool) {
|
||||
data, _ := result["data"].([]any)
|
||||
for _, raw := range data {
|
||||
item, _ := raw.(map[string]any)
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
}
|
||||
duration := floatFromAny(item["duration"])
|
||||
if duration <= 0 {
|
||||
continue
|
||||
}
|
||||
rounded := math.Round(duration)
|
||||
if rounded <= 0 {
|
||||
rounded = 1
|
||||
}
|
||||
return rounded, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func billingAudioEnabled(body map[string]any, response clients.Response) (bool, string) {
|
||||
if value, ok := generatedVideoHasAudio(response.Result); ok {
|
||||
return value, "generated_video"
|
||||
}
|
||||
return boolishValue(body["audio"]), "preprocessed_request"
|
||||
}
|
||||
|
||||
func generatedVideoHasAudio(result map[string]any) (bool, bool) {
|
||||
data, _ := result["data"].([]any)
|
||||
for _, raw := range data {
|
||||
item, _ := raw.(map[string]any)
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
}
|
||||
if value, ok := boolishOptional(firstPresentValue(item, "has_audio", "hasAudio")); ok {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func requestHasReferenceVideo(body map[string]any) bool {
|
||||
if hasNonEmptyArray(body["video_list"]) || hasNonEmptyArray(body["videoList"]) {
|
||||
return true
|
||||
@@ -367,8 +419,8 @@ func requestHasReferenceVideo(body map[string]any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func requestHasVoiceID(body map[string]any) bool {
|
||||
return boolishValue(body["audio"]) && firstNonEmptyStringValue(body, "voice_id", "voiceId") != ""
|
||||
func requestHasVoiceID(body map[string]any, audioEnabled bool) bool {
|
||||
return audioEnabled && firstNonEmptyStringValue(body, "voice_id", "voiceId") != ""
|
||||
}
|
||||
|
||||
func boolWeightKey(value bool) string {
|
||||
@@ -379,25 +431,38 @@ func boolWeightKey(value bool) string {
|
||||
}
|
||||
|
||||
func boolishValue(value any) bool {
|
||||
result, _ := boolishOptional(value)
|
||||
return result
|
||||
}
|
||||
|
||||
func boolishOptional(value any) (bool, bool) {
|
||||
switch typed := value.(type) {
|
||||
case bool:
|
||||
return typed
|
||||
return typed, true
|
||||
case string:
|
||||
switch strings.ToLower(strings.TrimSpace(typed)) {
|
||||
case "true", "1", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
return true, true
|
||||
case "false", "0", "no", "off":
|
||||
return false, true
|
||||
}
|
||||
case int:
|
||||
return typed != 0
|
||||
return typed != 0, true
|
||||
case int64:
|
||||
return typed != 0
|
||||
return typed != 0, true
|
||||
case float64:
|
||||
return typed != 0
|
||||
default:
|
||||
return false
|
||||
return typed != 0, true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func firstPresentValue(record map[string]any, keys ...string) any {
|
||||
for _, key := range keys {
|
||||
if value, ok := record[key]; ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasNonEmptyArray(value any) bool {
|
||||
|
||||
Reference in New Issue
Block a user