Skip to content

Commit 37d0852

Browse files
committed
fix: add default duration for audio command
Adds a default value for required duration on audio inference. Also refactors flag parsing to have shorthand names and use coba prerun for validation
1 parent 1817410 commit 37d0852

2 files changed

Lines changed: 76 additions & 67 deletions

File tree

cmd/constants.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ const (
2525
defaultImageDownloadTimeout = 1 * time.Minute
2626
defaultAudioDownloadTimeout = 5 * time.Minute
2727
defaultVideoDownloadTimeout = 10 * time.Minute
28+
29+
// Audio defaults.
30+
defaultMinAudioDuration = 10.0
31+
maxAudioDuration = 300.0
2832
)

cmd/inference_audio.go

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ import (
1515
"github.com/spf13/cobra"
1616
)
1717

18+
var audioFlags struct {
19+
model string
20+
duration float64
21+
count int
22+
outputDir string
23+
outputFormat string
24+
noDownload bool
25+
includeCost bool
26+
sampleRate int
27+
bitrate int
28+
preset string
29+
dryRun bool
30+
pollInterval time.Duration
31+
timeout time.Duration
32+
downloadTimeout time.Duration
33+
}
34+
1835
var audioInferenceCmd = &cobra.Command{
1936
Use: "audio [prompt]",
2037
Short: "Generate audio from text descriptions",
@@ -24,26 +41,27 @@ Examples:
2441
runware inference audio "a jazz piano solo with soft drums" --model elevenlabs:1@1 --duration 30
2542
runware inference audio "ocean waves crashing on rocks" --model elevenlabs:1@1 --duration 60
2643
runware inference audio "upbeat electronic music" --model elevenlabs:1@1 --duration 120 --sample-rate 48000`,
27-
Args: cobra.ExactArgs(1),
28-
RunE: runAudioInference,
44+
Args: cobra.ExactArgs(1),
45+
PreRunE: preRunAudioInference,
46+
RunE: runAudioInference,
2947
}
3048

3149
func init() {
3250
f := audioInferenceCmd.Flags()
33-
f.String("model", "", "Model identifier (e.g. elevenlabs:1@1)")
34-
f.Float64("duration", 0, "Audio duration in seconds (10-300)")
35-
f.Int("count", 1, "Number of audio files to generate (max 3)")
36-
f.String("output", "", "Output directory")
37-
f.String("output-format", "", "Audio format: mp3")
38-
f.Bool("no-download", false, "Print audio URLs instead of downloading")
39-
f.Bool("include-cost", false, "Include cost info in response")
40-
f.Int("sample-rate", 0, "Sample rate in Hz (8000-48000)")
41-
f.Int("bitrate", 0, "Bitrate in kbps (32-320, compressed formats only)")
42-
f.String("preset", "", "Named preset to apply")
43-
f.Bool("dry-run", false, "Print the API request without executing")
44-
f.Duration("poll-interval", defaultPollInterval, "Polling interval for async results")
45-
f.Duration("timeout", defaultAudioGenerationTimeout, "Maximum wait time for audio generation")
46-
f.Duration("download-timeout", defaultAudioDownloadTimeout, "timeout to use when downloading audio inference results")
51+
f.StringVarP(&audioFlags.model, "model", "m", "", "Model identifier (e.g. elevenlabs:1@1)")
52+
f.Float64VarP(&audioFlags.duration, "duration", "d", defaultMinAudioDuration, "Audio duration in seconds (10-300)")
53+
f.IntVarP(&audioFlags.count, "count", "n", 1, "Number of audio files to generate (max 3)")
54+
f.StringVarP(&audioFlags.outputDir, "output", "o", "", "Output directory")
55+
f.StringVarP(&audioFlags.outputFormat, "output-format", "f", "", "Audio format: mp3")
56+
f.BoolVarP(&audioFlags.noDownload, "no-download", "D", false, "Print audio URLs instead of downloading")
57+
f.BoolVarP(&audioFlags.includeCost, "include-cost", "c", false, "Include cost info in response")
58+
f.IntVarP(&audioFlags.sampleRate, "sample-rate", "r", 0, "Sample rate in Hz (8000-48000)")
59+
f.IntVarP(&audioFlags.bitrate, "bitrate", "b", 0, "Bitrate in kbps (32-320, compressed formats only)")
60+
f.StringVarP(&audioFlags.preset, "preset", "p", "", "Named preset to apply")
61+
f.BoolVarP(&audioFlags.dryRun, "dry-run", "X", false, "Print the API request without executing")
62+
f.DurationVarP(&audioFlags.pollInterval, "poll-interval", "i", defaultPollInterval, "Polling interval for async results")
63+
f.DurationVarP(&audioFlags.timeout, "timeout", "t", defaultAudioGenerationTimeout, "Maximum wait time for audio generation")
64+
f.DurationVarP(&audioFlags.downloadTimeout, "download-timeout", "T", defaultAudioDownloadTimeout, "Timeout for downloading audio results")
4765

4866
audioInferenceCmd.RegisterFlagCompletionFunc("output-format", func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) { //nolint:errcheck,gosec
4967
return []cobra.Completion{string(api.OutputFormatMP3)}, cobra.ShellCompDirectiveNoFileComp
@@ -52,6 +70,16 @@ func init() {
5270
audioInferenceCmd.RegisterFlagCompletionFunc("preset", completePresetNames) //nolint:errcheck,gosec
5371
}
5472

73+
func preRunAudioInference(_ *cobra.Command, _ []string) error {
74+
if audioFlags.duration < defaultMinAudioDuration || audioFlags.duration > maxAudioDuration {
75+
return fmt.Errorf("--duration must be between %.0f and %.0f seconds", defaultMinAudioDuration, maxAudioDuration)
76+
}
77+
if audioFlags.count < 1 || audioFlags.count > 3 {
78+
return fmt.Errorf("--count must be between 1 and 3")
79+
}
80+
return nil
81+
}
82+
5583
func runAudioInference(cmd *cobra.Command, args []string) error {
5684
ctx := cmd.Context()
5785
if ctx == nil {
@@ -71,11 +99,10 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
7199
outputDir := cfg.Defaults.OutputDir
72100

73101
// Apply preset if specified
74-
presetName, _ := cmd.Flags().GetString("preset")
75-
if presetName != "" {
76-
preset := config.GetPreset(presetName)
102+
if audioFlags.preset != "" {
103+
preset := config.GetPreset(audioFlags.preset)
77104
if preset == nil {
78-
return fmt.Errorf("preset '%s' not found", presetName)
105+
return fmt.Errorf("preset '%s' not found", audioFlags.preset)
79106
}
80107
if preset.Model != "" {
81108
model = preset.Model
@@ -84,57 +111,39 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
84111

85112
// Override with explicit CLI flags
86113
if cmd.Flags().Changed("model") {
87-
model, _ = cmd.Flags().GetString("model")
114+
model = audioFlags.model
88115
}
89116
if cmd.Flags().Changed("output") {
90-
outputDir, _ = cmd.Flags().GetString("output")
91-
}
92-
93-
duration, _ := cmd.Flags().GetFloat64("duration")
94-
count, _ := cmd.Flags().GetInt("count")
95-
outputFormat, _ := cmd.Flags().GetString("output-format")
96-
noDownload, _ := cmd.Flags().GetBool("no-download")
97-
includeCost, _ := cmd.Flags().GetBool("include-cost")
98-
dryRun, _ := cmd.Flags().GetBool("dry-run")
99-
sampleRate, _ := cmd.Flags().GetInt("sample-rate")
100-
bitrate, _ := cmd.Flags().GetInt("bitrate")
101-
pollInterval, _ := cmd.Flags().GetDuration("poll-interval")
102-
timeout, _ := cmd.Flags().GetDuration("timeout")
103-
downloadTimeout, _ := cmd.Flags().GetDuration("download-timeout")
104-
105-
// Validation
106-
if duration <= 0 {
107-
return fmt.Errorf("--duration is required (10-300 seconds)")
117+
outputDir = audioFlags.outputDir
108118
}
109119

110120
// Build request
111121
req := &api.AudioInferenceRequest{
112122
TaskUUID: api.NewUUID(),
113123
Model: model,
114124
PositivePrompt: prompt,
115-
Duration: duration,
125+
Duration: audioFlags.duration,
116126
DeliveryMethod: api.DeliveryMethodAsync,
117-
IncludeCost: includeCost,
127+
IncludeCost: audioFlags.includeCost,
128+
NumberResults: audioFlags.count,
118129
}
119130

120-
req.NumberResults = count
121-
if outputFormat != "" {
122-
req.OutputFormat = api.OutputFormat(outputFormat)
131+
if audioFlags.outputFormat != "" {
132+
req.OutputFormat = api.OutputFormat(audioFlags.outputFormat)
123133
}
124134

125-
// Audio settings
126-
if sampleRate > 0 || bitrate > 0 {
135+
if audioFlags.sampleRate > 0 || audioFlags.bitrate > 0 {
127136
req.AudioSettings = &api.AudioSettings{}
128-
if sampleRate > 0 {
129-
req.AudioSettings.SampleRate = sampleRate
137+
if audioFlags.sampleRate > 0 {
138+
req.AudioSettings.SampleRate = audioFlags.sampleRate
130139
}
131-
if bitrate > 0 {
132-
req.AudioSettings.Bitrate = bitrate
140+
if audioFlags.bitrate > 0 {
141+
req.AudioSettings.Bitrate = audioFlags.bitrate
133142
}
134143
}
135144

136145
// Dry run
137-
if dryRun {
146+
if audioFlags.dryRun {
138147
data, _ := json.MarshalIndent([]any{req}, "", " ")
139148
fmt.Println(string(data))
140149
return nil
@@ -148,7 +157,6 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
148157
_, err := client.AudioInference(context.Background(), req)
149158
if err != nil {
150159
s.Stop()
151-
152160
if api.IsAuthError(err) {
153161
output.Error("Authentication failed. Run 'runware auth login' to set your API key.")
154162
return err
@@ -158,16 +166,14 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
158166

159167
s.Suffix(" Generating audio (this may take a few minutes)...")
160168

161-
taskUUID := req.TaskUUID
162-
163-
pollCtx, cancel := context.WithTimeout(cmd.Context(), timeout)
169+
pollCtx, cancel := context.WithTimeout(cmd.Context(), audioFlags.timeout)
164170
defer cancel()
165171

166172
results, err := api.PollResults(
167173
pollCtx,
168174
client,
169-
taskUUID,
170-
pollInterval,
175+
req.TaskUUID,
176+
audioFlags.pollInterval,
171177
flagVerbose,
172178
func(raw json.RawMessage) (api.AudioInferenceResult, bool) {
173179
var r api.AudioInferenceResult
@@ -186,7 +192,7 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
186192
if len(results) == 0 {
187193
s.Stop()
188194
output.Error("Audio generation timed out or returned no results")
189-
return fmt.Errorf("no audio results after %v", timeout)
195+
return fmt.Errorf("no audio results after %v", audioFlags.timeout)
190196
}
191197

192198
// JSON/YAML output
@@ -196,21 +202,20 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
196202
return output.Print(format, results, nil, nil)
197203
}
198204

199-
// Table output
200-
if noDownload {
205+
// Table output — no-download: just print URLs
206+
if audioFlags.noDownload {
201207
headers := []any{tableHeaderNum, tableHeaderURL}
202-
if includeCost {
208+
if audioFlags.includeCost {
203209
headers = append(headers, tableHeaderCost)
204210
}
205211
var rows [][]any
206212
for i, r := range results {
207213
row := []any{i + 1, r.AudioURL}
208-
if includeCost {
214+
if audioFlags.includeCost {
209215
row = append(row, fmt.Sprintf("%.4f", r.Cost))
210216
}
211217
rows = append(rows, row)
212218
}
213-
214219
s.Stop()
215220
return output.Print(format, results, headers, rows)
216221
}
@@ -223,26 +228,26 @@ func runAudioInference(cmd *cobra.Command, args []string) error {
223228
}
224229

225230
headers := []any{tableHeaderNum, tableHeaderFile}
226-
if includeCost {
231+
if audioFlags.includeCost {
227232
headers = append(headers, tableHeaderCost)
228233
}
229234
var rows [][]any
230235

231236
for i, r := range results {
232-
ext := outputFormat
237+
ext := audioFlags.outputFormat
233238
if ext == "" {
234239
ext = string(api.OutputFormatMP3)
235240
}
236241
filename := fmt.Sprintf("runware_%s_%d.%s", time.Now().Format("20060102_150405"), i+1, ext)
237242
destPath := filepath.Join(outputDir, filename)
238243

239-
if err := rhttp.Download(ctx, r.AudioURL, destPath, downloadTimeout); err != nil {
244+
if err := rhttp.Download(ctx, r.AudioURL, destPath, audioFlags.downloadTimeout); err != nil {
240245
output.Error(fmt.Sprintf("Failed to download audio %d: %s", i+1, err))
241246
continue
242247
}
243248

244249
row := []any{i + 1, destPath}
245-
if includeCost {
250+
if audioFlags.includeCost {
246251
row = append(row, fmt.Sprintf("%.4f", r.Cost))
247252
}
248253
rows = append(rows, row)

0 commit comments

Comments
 (0)