Skip to content

Commit 82ed6f6

Browse files
committed
Updates
1 parent 7f42f8d commit 82ed6f6

File tree

13 files changed

+256
-168
lines changed

13 files changed

+256
-168
lines changed

cmd/cli/elevenlabs.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"regexp"
7+
"strings"
8+
9+
// Package imports
10+
"github.com/mutablelogic/go-client/pkg/client"
11+
"github.com/mutablelogic/go-client/pkg/elevenlabs"
12+
)
13+
14+
/////////////////////////////////////////////////////////////////////
15+
// TYPES
16+
17+
type result struct {
18+
Path string `json:"path"`
19+
Bytes int64 `json:"bytes_written"`
20+
Mime string `json:"mime_type,omitempty"`
21+
}
22+
23+
/////////////////////////////////////////////////////////////////////
24+
// GLOBALS
25+
26+
var (
27+
reVoiceId = regexp.MustCompile("^[a-zA-Z0-9]{20}$")
28+
)
29+
30+
/////////////////////////////////////////////////////////////////////
31+
// REGISTER FUNCTIONS
32+
33+
func ElevenlabsFlags(flags *Flags) {
34+
flags.String("elevenlabs-api-key", "${ELEVENLABS_API_KEY}", "ElevenLabs API key")
35+
}
36+
37+
func ElevenlabsRegister(cmd []Client, opts []client.ClientOpt, flags *Flags) ([]Client, error) {
38+
elevenlabs, err := elevenlabs.New(flags.GetString("elevenlabs-api-key"), opts...)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
// Register commands
44+
cmd = append(cmd, Client{
45+
ns: "elevenlabs",
46+
cmd: []Command{
47+
{Name: "voices", Description: "Return registered voices", MinArgs: 2, MaxArgs: 2, Fn: elVoices(elevenlabs, flags)},
48+
{Name: "voice", Description: "Return voice information", Syntax: "<voice-id>", MinArgs: 3, MaxArgs: 3, Fn: elVoice(elevenlabs, flags)},
49+
{Name: "speak", Description: "Create speech from a prompt", Syntax: "<voice> <prompt>", MinArgs: 4, MaxArgs: 4, Fn: elSpeak(elevenlabs, flags)},
50+
},
51+
})
52+
53+
// Return success
54+
return cmd, nil
55+
}
56+
57+
/////////////////////////////////////////////////////////////////////
58+
// API CALL FUNCTIONS
59+
60+
func elVoices(client *elevenlabs.Client, flags *Flags) CommandFn {
61+
return func() error {
62+
if voices, err := client.Voices(); err != nil {
63+
return err
64+
} else {
65+
return flags.Write(voices)
66+
}
67+
}
68+
}
69+
70+
func elVoice(client *elevenlabs.Client, flags *Flags) CommandFn {
71+
return func() error {
72+
if voice, err := client.Voice(flags.Arg(2)); err != nil {
73+
return err
74+
} else {
75+
return flags.Write(voice)
76+
}
77+
}
78+
}
79+
80+
func elSpeak(client *elevenlabs.Client, flags *Flags) CommandFn {
81+
return func() error {
82+
voice, err := elGetVoiceId(client, flags.Arg(2))
83+
if err != nil {
84+
return err
85+
}
86+
87+
// Set options
88+
opts := []elevenlabs.Opt{}
89+
90+
// Create the audio
91+
out := flags.GetOutFilename("speech.mp3", 0)
92+
file, err := os.Create(out)
93+
if err != nil {
94+
return err
95+
}
96+
defer file.Close()
97+
if n, err := client.TextToSpeech(file, voice, flags.Arg(3), opts...); err != nil {
98+
return err
99+
} else if err := flags.Write(result{Path: out, Bytes: n}); err != nil {
100+
return err
101+
}
102+
103+
// Return success
104+
return nil
105+
}
106+
}
107+
108+
/////////////////////////////////////////////////////////////////////
109+
// PRIVATE METHODS
110+
111+
// return a voice-id given a parameter, which can be a voice-id or name
112+
func elGetVoiceId(client *elevenlabs.Client, voice string) (string, error) {
113+
if reVoiceId.MatchString(voice) {
114+
return voice, nil
115+
} else if voices, err := client.Voices(); err != nil {
116+
return "", err
117+
} else {
118+
for _, v := range voices {
119+
if strings.EqualFold(v.Name, voice) || v.Id == voice {
120+
return v.Id, nil
121+
}
122+
}
123+
}
124+
return "", fmt.Errorf("voice not found: %q", voice)
125+
}

cmd/cli/main.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import (
66
"os"
77
"path"
88

9+
// Packages
910
"github.com/mutablelogic/go-client/pkg/client"
1011
"github.com/pkg/errors"
1112
)
1213

1314
func main() {
1415
name := path.Base(os.Args[0])
15-
flags, err := NewFlags(name, os.Args[1:], OpenAIFlags, HomeAssistantFlags)
16+
flags, err := NewFlags(name, os.Args[1:], OpenAIFlags, ElevenlabsFlags, HomeAssistantFlags)
1617
if err != nil {
1718
if err != flag.ErrHelp {
1819
fmt.Fprintln(os.Stderr, err)
@@ -33,21 +34,25 @@ func main() {
3334

3435
// Register commands
3536
var cmd []Client
37+
3638
cmd, err = IpifyRegister(cmd, opts, flags)
3739
if err != nil {
3840
fmt.Fprintln(os.Stderr, err)
3941
os.Exit(1)
4042
}
41-
/* cmd, err = ElevenlabsRegister(cmd, opts, flags)
42-
if err != nil {
43-
fmt.Fprintln(os.Stderr, err)
44-
os.Exit(1)
45-
}*/
43+
44+
cmd, err = ElevenlabsRegister(cmd, opts, flags)
45+
if err != nil {
46+
fmt.Fprintln(os.Stderr, err)
47+
os.Exit(1)
48+
}
49+
4650
cmd, err = OpenAIRegister(cmd, opts, flags)
4751
if err != nil {
4852
fmt.Fprintln(os.Stderr, err)
4953
os.Exit(1)
5054
}
55+
5156
cmd, err = HomeAssistantRegister(cmd, opts, flags)
5257
if err != nil {
5358
fmt.Fprintln(os.Stderr, err)

cmd/cli/openai.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"errors"
5-
"fmt"
65
"net/url"
76
"os"
87
"path/filepath"
@@ -189,28 +188,25 @@ func openaiSpeak(client *openai.Client, flags *Flags) CommandFn {
189188
prompt = flags.Arg(2)
190189
}
191190

192-
// Determine the filename
191+
// Determine the filename // TODO
193192
w, err := os.Create("output.mp3")
194193
if err != nil {
195194
return err
196195
}
197196
defer w.Close()
198197

199198
// Create the audio
200-
response, err := client.Speech(w, voice, prompt, opts...)
201-
if err != nil {
199+
if _, err := client.Speech(w, voice, prompt, opts...); err != nil {
202200
return err
203201
}
204202

205-
// Open images
203+
// Open the audio
206204
if flags.GetBool("open") {
207205
if err := open("output.mp3"); err != nil {
208206
return err
209207
}
210208
}
211209

212-
fmt.Println(response, "bytes written")
213-
214210
// Return any errors
215211
return nil
216212
}

go.mod

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@ go 1.21.5
55
require (
66
github.com/djthorpe/go-errors v1.0.3
77
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517
8-
github.com/mattn/go-runewidth v0.0.15
98
github.com/pkg/errors v0.9.1
109
github.com/stretchr/testify v1.9.0
11-
github.com/veandco/go-sdl2 v0.4.36
12-
golang.org/x/exp v0.0.0-20231127185646-65229373498e
13-
golang.org/x/term v0.15.0
1410
)
1511

1612
require (
1713
github.com/davecgh/go-spew v1.1.1 // indirect
1814
github.com/pmezard/go-difflib v1.0.0 // indirect
19-
github.com/rivo/uniseg v0.2.0 // indirect
20-
golang.org/x/sys v0.15.0 // indirect
2115
gopkg.in/yaml.v3 v3.0.1 // indirect
2216
)

go.sum

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,12 @@ github.com/djthorpe/go-errors v1.0.3 h1:GZeMPkC1mx2vteXLI/gvxZS0Ee9zxzwD1mcYyKU5
44
github.com/djthorpe/go-errors v1.0.3/go.mod h1:HtfrZnMd6HsX75Mtbv9Qcnn0BqOrrFArvCaj3RMnZhY=
55
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517 h1:UOUbtOLKMmupwpbYq1YG28pXDHJOlrznNX2vavHGhKk=
66
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517/go.mod h1:M0TwrksgC73IdXcj9ZXtpuw2Tkl7jdQi/mFPOo1I/4M=
7-
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
8-
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
97
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
108
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
119
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1210
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
13-
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
14-
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
1511
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
1612
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
17-
github.com/veandco/go-sdl2 v0.4.36 h1:Ltydev536rRQodmIrTWFZ3dRp5A+/6t5CYvbi4Kvia0=
18-
github.com/veandco/go-sdl2 v0.4.36/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
19-
golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No=
20-
golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
21-
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
22-
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
23-
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
24-
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
2513
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2614
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2715
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

output.mp3

-9.38 KB
Binary file not shown.

pkg/client/transport.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ func (transport *logtransport) RoundTrip(req *http.Request) (*http.Response, err
109109
// PRIVATE METHODS
110110

111111
func (w *readwrapper) Read(b []byte) (int, error) {
112+
if w.r == nil {
113+
return 0, io.EOF
114+
}
112115
n, err := w.r.Read(b)
113116
if err == nil {
114117
_, err = w.data.Write(b[:n])
@@ -117,7 +120,11 @@ func (w *readwrapper) Read(b []byte) (int, error) {
117120
}
118121

119122
func (w *readwrapper) Close() error {
120-
return w.r.Close()
123+
if w.r != nil {
124+
return w.r.Close()
125+
} else {
126+
return nil
127+
}
121128
}
122129

123130
func (w *readwrapper) as(mimetype string) ([]byte, error) {

pkg/elevenlabs/opts.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package elevenlabs
2+
3+
///////////////////////////////////////////////////////////////////////////////
4+
// TYPES
5+
6+
type opts struct {
7+
Model string `json:"model_id,omitempty"`
8+
Seed uint `json:"seed,omitempty"`
9+
}
10+
11+
// Opt is a function which can be used to set options on a request
12+
type Opt func(*opts) error
13+
14+
///////////////////////////////////////////////////////////////////////////////
15+
// OPTIONS
16+
17+
// Set the voice model
18+
func OptModel(v string) Opt {
19+
return func(o *opts) error {
20+
o.Model = v
21+
return nil
22+
}
23+
}
24+
25+
// Set the deterministic seed
26+
func OptSeed(v uint) Opt {
27+
return func(o *opts) error {
28+
o.Seed = v
29+
return nil
30+
}
31+
}

0 commit comments

Comments
 (0)