diff --git a/src/commands/speech/synthesize.ts b/src/commands/speech/synthesize.ts index 8f968ea4..9d46f787 100644 --- a/src/commands/speech/synthesize.ts +++ b/src/commands/speech/synthesize.ts @@ -92,10 +92,9 @@ export default defineCommand({ if (flags.subtitles) body.subtitle_enable = true; // Correct API parameter name if (flags.pronunciation) { - body.pronunciation_dict = (flags.pronunciation as string[]).map(p => { - const [from, to] = p.split('/'); - return { tone: to || from!, text: from! }; - }); + body.pronunciation_dict = { + tone: flags.pronunciation as string[], + }; } if (dryRun(config, body)) return; diff --git a/src/types/api.ts b/src/types/api.ts index 2a96ecef..a6e0d619 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -105,7 +105,7 @@ export interface SpeechRequest { channel?: number; }; language_boost?: string; - pronunciation_dict?: Array<{ tone: string; text: string }>; + pronunciation_dict?: { tone: string[] }; output_format?: 'url' | 'hex'; stream?: boolean; subtitle_enable?: boolean; // Correct API parameter name (not 'subtitle') diff --git a/test/commands/speech/synthesize.test.ts b/test/commands/speech/synthesize.test.ts index 8a490630..81eb00b9 100644 --- a/test/commands/speech/synthesize.test.ts +++ b/test/commands/speech/synthesize.test.ts @@ -202,6 +202,105 @@ describe('speech synthesize command', () => { console.log = originalLog; } }); + + it('--pronunciation serializes multiple values with the API-compatible shape', async () => { + const config = { + apiKey: 'test-key', + region: 'global' as const, + baseUrl: 'https://api.mmx.io', + output: 'json' as const, + timeout: 10, + verbose: false, + quiet: false, + noColor: true, + yes: false, + dryRun: true, + nonInteractive: true, + async: false, + }; + + const originalLog = console.log; + let output = ''; + console.log = (msg: string) => { output += msg; }; + + try { + await synthesizeCommand.execute(config, { + text: '处理这个危险的情况。', + pronunciation: [ + '处理/(chu3)(li3)', + '危险/dangerous', + ], + quiet: false, + verbose: false, + noColor: true, + yes: false, + dryRun: true, + help: false, + nonInteractive: true, + async: false, + }); + + const parsed = JSON.parse(output); + + // pronunciation_dict is an object with a tone string array — not the + // old array-of-{text,tone} shape — and each value is preserved verbatim + // (no splitting, trimming, or rewriting), covering both the pinyin form + // and the plain-replacement form shown in the official API docs. + expect(Array.isArray(parsed.request.pronunciation_dict)).toBe(false); + expect(parsed.request.pronunciation_dict).toEqual({ + tone: [ + '处理/(chu3)(li3)', + '危险/dangerous', + ], + }); + } finally { + console.log = originalLog; + } + }); + + it('--pronunciation with a single value serializes under tone', async () => { + const config = { + apiKey: 'test-key', + region: 'global' as const, + baseUrl: 'https://api.mmx.io', + output: 'json' as const, + timeout: 10, + verbose: false, + quiet: false, + noColor: true, + yes: false, + dryRun: true, + nonInteractive: true, + async: false, + }; + + const originalLog = console.log; + let output = ''; + console.log = (msg: string) => { output += msg; }; + + try { + await synthesizeCommand.execute(config, { + text: 'Omg, the real danger is not that computers start thinking.', + pronunciation: ['Omg/Oh my god'], + quiet: false, + verbose: false, + noColor: true, + yes: false, + dryRun: true, + help: false, + nonInteractive: true, + async: false, + }); + + const parsed = JSON.parse(output); + + expect(parsed.request.pronunciation_dict).toEqual({ + tone: ['Omg/Oh my god'], + }); + } finally { + console.log = originalLog; + } + }); }); describe('speech synthesize format validation', () => {