@@ -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+
1835var 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
3149func 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+
5583func 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