@@ -3,14 +3,14 @@ import { defineCommand } from '../../command';
33import { CLIError } from '../../errors/base' ;
44import { ExitCode } from '../../errors/codes' ;
55import { request , requestJson } from '../../client/http' ;
6- import { musicEndpoint } from '../../client/endpoints' ;
6+ import { musicCoverPreprocessEndpoint , musicEndpoint } from '../../client/endpoints' ;
77import { formatOutput , detectOutputFormat } from '../../output/formatter' ;
88import { saveAudioOutput } from '../../output/audio' ;
99import { MUSIC_FORMATS , formatList , validateAudioFormat } from '../../utils/audio-formats' ;
1010import { pipeAudioStream } from '../../utils/audio-stream' ;
1111import type { Config } from '../../config/schema' ;
1212import type { GlobalFlags } from '../../types/flags' ;
13- import type { MusicRequest , MusicResponse } from '../../types/api' ;
13+ import type { CoverPreprocessRequest , CoverPreprocessResponse , MusicRequest , MusicResponse } from '../../types/api' ;
1414import { musicCoverModel } from './models' ;
1515
1616export default defineCommand ( {
@@ -23,8 +23,8 @@ export default defineCommand({
2323 { flag : '--prompt <text>' , description : 'Target cover style, e.g. "Indie folk, acoustic guitar, warm male vocal"' } ,
2424 { flag : '--audio <url>' , description : 'URL of the reference audio (mp3, wav, flac, etc. — 6s to 6min, max 50MB)' } ,
2525 { flag : '--audio-file <path>' , description : 'Local reference audio file (auto base64-encoded)' } ,
26- { flag : '--lyrics <text>' , description : 'Cover lyrics. If omitted, extracted from reference audio via ASR.' } ,
27- { flag : '--lyrics-file <path>' , description : 'Read lyrics from file (use - for stdin)' } ,
26+ { flag : '--lyrics <text>' , description : 'Cover lyrics. If provided, CLI auto-runs cover preprocess first. If omitted, lyrics are extracted from reference audio via ASR.' } ,
27+ { flag : '--lyrics-file <path>' , description : 'Read lyrics from file (use - for stdin). Triggers cover preprocess automatically. ' } ,
2828 { flag : '--seed <number>' , description : 'Random seed 0–1000000 for reproducible results' , type : 'number' } ,
2929 { flag : '--format <fmt>' , description : `Audio format: ${ formatList ( MUSIC_FORMATS ) } (default: mp3)` } ,
3030 { flag : '--sample-rate <hz>' , description : 'Sample rate: 16000, 24000, 32000, 44100 (default: 44100)' , type : 'number' } ,
@@ -80,6 +80,10 @@ export default defineCommand({
8080 'mmx music cover --model music-cover' ,
8181 ) ;
8282 }
83+ const audioSource : Pick < MusicRequest , 'audio_url' | 'audio_base64' > = audioUrl
84+ ? { audio_url : audioUrl }
85+ : { audio_base64 : readFileSync ( audioFile ! ) . toString ( 'base64' ) } ;
86+
8387 const body : MusicRequest = {
8488 model,
8589 prompt,
@@ -93,21 +97,41 @@ export default defineCommand({
9397 } ,
9498 output_format : 'hex' ,
9599 stream : flags . stream === true ,
100+ ...audioSource ,
96101 } ;
97102
98- if ( audioUrl ) {
99- body . audio_url = audioUrl ;
100- } else {
101- body . audio_base64 = readFileSync ( audioFile ! ) . toString ( 'base64' ) ;
103+ const needsPreprocess = Boolean ( lyrics ?. trim ( ) ) ;
104+ const preprocessBody : CoverPreprocessRequest | undefined = needsPreprocess
105+ ? {
106+ model : 'music-cover' ,
107+ ...audioSource ,
108+ }
109+ : undefined ;
110+
111+ if ( needsPreprocess ) {
112+ delete body . audio_url ;
113+ delete body . audio_base64 ;
114+ body . cover_feature_id = '__cover_feature_id_from_preprocess__' ;
102115 }
103116
104117 if ( config . dryRun ) {
105- console . log ( formatOutput ( { request : body } , format ) ) ;
118+ console . log ( formatOutput ( needsPreprocess ? { preprocess_request : preprocessBody , request : body } : { request : body } , format ) ) ;
106119 return ;
107120 }
108121
109122 const url = musicEndpoint ( config . baseUrl ) ;
110123
124+ if ( needsPreprocess ) {
125+ const preprocessUrl = musicCoverPreprocessEndpoint ( config . baseUrl ) ;
126+ const preprocessResponse = await requestJson < CoverPreprocessResponse > ( config , {
127+ url : preprocessUrl ,
128+ method : 'POST' ,
129+ body : preprocessBody ,
130+ } ) ;
131+
132+ body . cover_feature_id = preprocessResponse . cover_feature_id ;
133+ }
134+
111135 if ( flags . stream ) {
112136 const res = await request ( config , { url, method : 'POST' , body, stream : true } ) ;
113137 await pipeAudioStream ( res ) ;
0 commit comments