Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions cmd/speak.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ func init() {
return ensureAPIKey()
},
RunE: func(cmd *cobra.Command, args []string) error {
if opts.speed <= 0.5 || opts.speed >= 2.0 {
return errors.New("speed must be between 0.5 and 2.0 (e.g. 1.1 for 10% faster)")
}
if opts.rateWPM > 0 {
// Map macOS `say` rate (words per minute) to ElevenLabs speed multiplier.
opts.speed = float64(opts.rateWPM) / float64(defaultWPM)
if opts.speed <= 0.5 || opts.speed >= 2.0 {
return fmt.Errorf("rate %d wpm maps to speed %.2f, which is outside the allowed 0.5–2.0 range", opts.rateWPM, opts.speed)
}
} else if opts.speed <= 0.5 || opts.speed >= 2.0 {
return errors.New("speed must be between 0.5 and 2.0 (e.g. 1.1 for 10% faster)")
}

if opts.voiceID == "" {
Expand Down Expand Up @@ -139,7 +138,7 @@ func init() {
}

cmd.Flags().StringVar(&opts.voiceID, "voice-id", "", "Voice ID to use (ELEVENLABS_VOICE_ID)")
cmd.Flags().StringVarP(&opts.voiceID, "voice", "v", opts.voiceID, "Alias for --voice-id; accepts name or ID; use '?' to list voices")
cmd.Flags().StringVarP(&opts.voiceID, "voice", "v", "", "Alias for --voice-id; accepts name or ID; use '?' to list voices")
cmd.Flags().StringVar(&opts.modelID, "model-id", opts.modelID, "Model ID (default: eleven_v3). Common: eleven_multilingual_v2 (stable), eleven_flash_v2_5 (fast/cheap), eleven_turbo_v2_5 (balanced).")
cmd.Flags().StringVarP(&opts.outputPath, "output", "o", "", "Write audio to file (disables playback unless --play is also set)")
cmd.Flags().StringVar(&opts.outputFmt, "format", opts.outputFmt, "Output format (e.g. mp3_44100_128)")
Expand Down Expand Up @@ -451,8 +450,8 @@ func resolveVoice(ctx context.Context, client *elevenlabs.Client, voiceInput str
return "", nil
}

// If input looks like an ID (UUID-like), use directly.
if len(voiceInput) >= 15 && strings.ContainsAny(voiceInput, "0123456789") {
// If input looks like an ID (alphanumeric >= 15 chars without spaces), use directly.
if len(voiceInput) >= 15 && !strings.ContainsRune(voiceInput, ' ') {
return voiceInput, nil
Comment on lines +453 to 455

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid treating long single-word names as IDs

The new heuristic treats any input with length ≥15 and no spaces as a voice ID, skipping the name lookup entirely. This regresses cases where a valid voice name is a single long token (e.g., a custom name like “LongSingleWordName”), which previously would be resolved via ListVoices but now is sent directly as an ID and will fail or pick the wrong voice. Consider tightening the ID check to a specific ID pattern (e.g., UUID/known charset) or keeping name lookup for non-matching IDs.

Useful? React with 👍 / 👎.

}

Expand Down