mirror of
https://github.com/taigrr/elevenlabs.git
synced 2026-04-02 11:19:01 -07:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae5a1f2218 | |||
| 7e25beb0a7 | |||
|
b179ed7944
|
|||
| c5609d07e9 | |||
|
|
d79325ad71 | ||
| fba03ed0be | |||
| aa701237ff | |||
|
|
29fa401714 | ||
| 77d17e20fb | |||
|
|
93af72dc7c | ||
|
|
db0a2e1760 | ||
|
|
c585531fae | ||
|
|
41f142ec2c | ||
|
6ebcddb891
|
|||
|
ae598ecc4b
|
63
README.md
63
README.md
@@ -1,10 +1,10 @@
|
|||||||
# elevenlabs
|
# elevenlabs
|
||||||
|
|
||||||
[](https://opensource.org/licenses/0BSD)
|
[](https://opensource.org/licenses/0BSD)
|
||||||
[](https://godoc.org/github.com/taigrr/elevenlabs)
|
[](https://godoc.org/github.com/taigrr/elevenlabs)
|
||||||
[](go.mod)
|
[](go.mod)
|
||||||
[](https://goreportcard.com/report/github.com/taigrr/elevenlabs)
|
[](https://goreportcard.com/report/github.com/taigrr/elevenlabs)
|
||||||
|
|
||||||
|
|
||||||
Unofficial [elevenlabs.io](https://beta.elevenlabs.io/) ([11.ai](http://11.ai)) voice synthesis client
|
Unofficial [elevenlabs.io](https://beta.elevenlabs.io/) ([11.ai](http://11.ai)) voice synthesis client
|
||||||
|
|
||||||
This library is not affiliated with, nor associated with ElevenLabs in any way.
|
This library is not affiliated with, nor associated with ElevenLabs in any way.
|
||||||
@@ -13,10 +13,10 @@ ElevenLabs' official api documentation, upon which this client has been
|
|||||||
derived, [can be found here](https://api.elevenlabs.io/docs).
|
derived, [can be found here](https://api.elevenlabs.io/docs).
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
This go client provides an easy interface to create synthesized voices and
|
This go client provides an easy interface to create synthesized voices and
|
||||||
make TTS (text-to-speech) requests to elevenlabs.io
|
make TTS (text-to-speech) requests to elevenlabs.io
|
||||||
|
|
||||||
|
|
||||||
As a prerequisite, you must already have an account with elevenlabs.io.
|
As a prerequisite, you must already have an account with elevenlabs.io.
|
||||||
After creating your account, you can get your API key [from here](https://help.elevenlabs.io/hc/en-us/articles/14599447207697-How-to-authorize-yourself-using-your-xi-api-key-).
|
After creating your account, you can get your API key [from here](https://help.elevenlabs.io/hc/en-us/articles/14599447207697-How-to-authorize-yourself-using-your-xi-api-key-).
|
||||||
|
|
||||||
@@ -30,11 +30,14 @@ Set the `XI_API_KEY` environment variable, and pipe it some text to give it a wh
|
|||||||
|
|
||||||
## Example Code
|
## Example Code
|
||||||
|
|
||||||
|
### Text-to-Speech Example
|
||||||
|
|
||||||
To use this library, create a new client and send a TTS request to a voice.
|
To use this library, create a new client and send a TTS request to a voice.
|
||||||
The following code block illustrates how one might replicate the say/espeak
|
The following code block illustrates how one might replicate the say/espeak
|
||||||
command, using the streaming endpoint.
|
command, using the streaming endpoint.
|
||||||
I've opted to go with faiface's beep package, but you can also save the file
|
I've opted to go with gopxl's beep package, but you can also save the file
|
||||||
to an mp3 on-disk.
|
to an mp3 on-disk.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -46,9 +49,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/faiface/beep"
|
"github.com/gopxl/beep/v2"
|
||||||
"github.com/faiface/beep/mp3"
|
"github.com/gopxl/beep/v2/mp3"
|
||||||
"github.com/faiface/beep/speaker"
|
"github.com/gopxl/beep/v2/speaker"
|
||||||
|
|
||||||
"github.com/taigrr/elevenlabs/client"
|
"github.com/taigrr/elevenlabs/client"
|
||||||
"github.com/taigrr/elevenlabs/client/types"
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
@@ -69,7 +72,7 @@ func main() {
|
|||||||
text, _ := reader.ReadString('\n')
|
text, _ := reader.ReadString('\n')
|
||||||
go func() {
|
go func() {
|
||||||
// stream audio from elevenlabs using the first voice we found
|
// stream audio from elevenlabs using the first voice we found
|
||||||
err = client.TTSStream(ctx, pipeWriter, text, ids[0], types.SynthesisOptions{Stability: 0.75, SimilarityBoost: 0.75})
|
err = client.TTSStream(ctx, pipeWriter, text, ids[0], types.SynthesisOptions{Stability: 0.75, SimilarityBoost: 0.75, Style: 0.0, UseSpeakerBoost: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -90,3 +93,49 @@ func main() {
|
|||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Sound Generation Example
|
||||||
|
|
||||||
|
The following example demonstrates how to generate sound effects using the Sound Generation API:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/taigrr/elevenlabs/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
// Create a new client with your API key
|
||||||
|
client := client.New(os.Getenv("XI_API_KEY"))
|
||||||
|
|
||||||
|
// Generate a sound effect and save it to a file
|
||||||
|
f, err := os.Create("footsteps.mp3")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Basic usage (using default duration and prompt influence)
|
||||||
|
err = client.SoundGenerationWriter(ctx, f, "footsteps on wooden floor", 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced usage with custom duration and prompt influence
|
||||||
|
audio, err := client.SoundGeneration(
|
||||||
|
ctx,
|
||||||
|
"heavy rain on a tin roof",
|
||||||
|
5.0, // Set duration to 5 seconds
|
||||||
|
0.5, // Set prompt influence to 0.5
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.WriteFile("rain.mp3", audio, 0644)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
const apiEndpoint = "https://api.elevenlabs.io"
|
const apiEndpoint = "https://api.elevenlabs.io"
|
||||||
@@ -12,14 +13,16 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
endpoint string
|
endpoint string
|
||||||
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(apiKey string) Client {
|
func New(apiKey string) Client {
|
||||||
return Client{
|
return Client{
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
endpoint: apiEndpoint,
|
endpoint: apiEndpoint,
|
||||||
|
httpClient: &http.Client{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,3 +30,14 @@ func (c Client) WithEndpoint(endpoint string) Client {
|
|||||||
c.endpoint = endpoint
|
c.endpoint = endpoint
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Client) WithAPIKey(apiKey string) Client {
|
||||||
|
c.apiKey = apiKey
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHTTPClient allows users to provide their own http.Client
|
||||||
|
func (c Client) WithHTTPClient(hc *http.Client) Client {
|
||||||
|
c.httpClient = hc
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import (
|
|||||||
|
|
||||||
func (c Client) HistoryDelete(ctx context.Context, historyItemID string) (bool, error) {
|
func (c Client) HistoryDelete(ctx context.Context, historyItemID string) (bool, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/history/%s", historyItemID)
|
url := fmt.Sprintf(c.endpoint+"/v1/history/%s", historyItemID)
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -24,7 +22,7 @@ func (c Client) HistoryDelete(ctx context.Context, historyItemID string) (bool,
|
|||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
|
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
@@ -55,7 +53,6 @@ func (c Client) HistoryDownloadZipWriter(ctx context.Context, w io.Writer, id1,
|
|||||||
toDownload := types.HistoryPost{
|
toDownload := types.HistoryPost{
|
||||||
HistoryItemIds: downloads,
|
HistoryItemIds: downloads,
|
||||||
}
|
}
|
||||||
client := &http.Client{}
|
|
||||||
body, _ := json.Marshal(toDownload)
|
body, _ := json.Marshal(toDownload)
|
||||||
bodyReader := bytes.NewReader(body)
|
bodyReader := bytes.NewReader(body)
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
|
||||||
@@ -66,7 +63,7 @@ func (c Client) HistoryDownloadZipWriter(ctx context.Context, w io.Writer, id1,
|
|||||||
req.Header.Set("accept", "archive/zip")
|
req.Header.Set("accept", "archive/zip")
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
|
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
@@ -99,7 +96,6 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
|
|||||||
toDownload := types.HistoryPost{
|
toDownload := types.HistoryPost{
|
||||||
HistoryItemIds: downloads,
|
HistoryItemIds: downloads,
|
||||||
}
|
}
|
||||||
client := &http.Client{}
|
|
||||||
body, _ := json.Marshal(toDownload)
|
body, _ := json.Marshal(toDownload)
|
||||||
bodyReader := bytes.NewReader(body)
|
bodyReader := bytes.NewReader(body)
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
|
||||||
@@ -110,7 +106,7 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
|
|||||||
req.Header.Set("accept", "archive/zip")
|
req.Header.Set("accept", "archive/zip")
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
|
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
@@ -142,7 +138,6 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
|
|||||||
|
|
||||||
func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID string) error {
|
func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID string) error {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
|
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -150,7 +145,7 @@ func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -179,7 +174,6 @@ func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID
|
|||||||
|
|
||||||
func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, error) {
|
func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
|
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
@@ -187,7 +181,7 @@ func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, er
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
@@ -219,7 +213,6 @@ func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, er
|
|||||||
|
|
||||||
func (c Client) GetHistoryItemList(ctx context.Context) ([]types.HistoryItemList, error) {
|
func (c Client) GetHistoryItemList(ctx context.Context) ([]types.HistoryItemList, error) {
|
||||||
url := c.endpoint + "/v1/history"
|
url := c.endpoint + "/v1/history"
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []types.HistoryItemList{}, err
|
return []types.HistoryItemList{}, err
|
||||||
@@ -227,7 +220,7 @@ func (c Client) GetHistoryItemList(ctx context.Context) ([]types.HistoryItemList
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []types.HistoryItemList{}, err
|
return []types.HistoryItemList{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string) (bool, error) {
|
func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string) (bool, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s", voiceID, sampleID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s", voiceID, sampleID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -24,7 +23,7 @@ func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string)
|
|||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -50,7 +49,6 @@ func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string)
|
|||||||
|
|
||||||
func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voiceID, sampleID string) error {
|
func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voiceID, sampleID string) error {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -58,7 +56,7 @@ func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voic
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -87,7 +85,6 @@ func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voic
|
|||||||
|
|
||||||
func (c Client) DownloadVoiceSample(ctx context.Context, voiceID, sampleID string) ([]byte, error) {
|
func (c Client) DownloadVoiceSample(ctx context.Context, voiceID, sampleID string) ([]byte, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
@@ -95,7 +92,7 @@ func (c Client) DownloadVoiceSample(ctx context.Context, voiceID, sampleID strin
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
96
client/sound_gen.go
Normal file
96
client/sound_gen.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SoundGenerationWriter generates a sound effect from text and writes it to the provided writer.
|
||||||
|
// If durationSeconds is 0, it will be omitted from the request and the API will determine the optimal duration.
|
||||||
|
// If promptInfluence is 0, it will default to 0.3.
|
||||||
|
func (c Client) SoundGenerationWriter(ctx context.Context, w io.Writer, text string, durationSeconds, promptInfluence float64) error {
|
||||||
|
params := types.SoundGeneration{
|
||||||
|
Text: text,
|
||||||
|
PromptInfluence: 0.3, // default value
|
||||||
|
}
|
||||||
|
|
||||||
|
if promptInfluence != 0 {
|
||||||
|
params.PromptInfluence = promptInfluence
|
||||||
|
}
|
||||||
|
if durationSeconds != 0 {
|
||||||
|
params.DurationSeconds = durationSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.requestSoundGeneration(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
_, err = io.Copy(w, body)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SoundGeneration generates a sound effect from text and returns the audio as bytes.
|
||||||
|
// If durationSeconds is 0, it will be omitted from the request and the API will determine the optimal duration.
|
||||||
|
// If promptInfluence is 0, it will default to 0.3.
|
||||||
|
func (c Client) SoundGeneration(ctx context.Context, text string, durationSeconds, promptInfluence float64) ([]byte, error) {
|
||||||
|
params := types.SoundGeneration{
|
||||||
|
Text: text,
|
||||||
|
PromptInfluence: 0.3, // default value
|
||||||
|
}
|
||||||
|
|
||||||
|
if promptInfluence != 0 {
|
||||||
|
params.PromptInfluence = promptInfluence
|
||||||
|
}
|
||||||
|
if durationSeconds != 0 {
|
||||||
|
params.DurationSeconds = durationSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.requestSoundGeneration(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
_, err = io.Copy(&b, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) requestSoundGeneration(ctx context.Context, params types.SoundGeneration) (io.ReadCloser, error) {
|
||||||
|
url := c.endpoint + "/v1/sound-generation"
|
||||||
|
b, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
|
|
||||||
|
res, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
res.Body.Close()
|
||||||
|
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Body, nil
|
||||||
|
}
|
||||||
126
client/stt.go
Normal file
126
client/stt.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConvertSpeechToText converts audio to text using the specified file path
|
||||||
|
func (c *Client) ConvertSpeechToText(ctx context.Context, audioFilePath string, request types.SpeechToTextRequest) (*types.SpeechToTextResponse, error) {
|
||||||
|
file, err := os.Open(audioFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open audio file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return c.ConvertSpeechToTextFromReader(ctx, file, filepath.Base(audioFilePath), request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertSpeechToTextFromReader converts audio to text using the provided reader
|
||||||
|
func (c *Client) ConvertSpeechToTextFromReader(ctx context.Context, reader io.Reader, filename string, request types.SpeechToTextRequest) (*types.SpeechToTextResponse, error) {
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
|
||||||
|
if err := writer.WriteField("model_id", string(request.ModelID)); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write model_id field: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
part, err := writer.CreateFormFile("file", filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create form file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(part, reader); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to copy audio data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.LanguageCode != "" {
|
||||||
|
if err := writer.WriteField("language_code", request.LanguageCode); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write language_code field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.NumSpeakers != 0 {
|
||||||
|
if err := writer.WriteField("num_speakers", fmt.Sprintf("%d", request.NumSpeakers)); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write num_speakers field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if request.TagAudioEvents {
|
||||||
|
if err := writer.WriteField("tag_audio_events", "true"); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write tag_audio_events field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if request.TimestampsGranularity != "" {
|
||||||
|
if err := writer.WriteField("timestamps_granularity", string(request.TimestampsGranularity)); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write timestamps_granularity field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if request.Diarize {
|
||||||
|
if err := writer.WriteField("diarize", "true"); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write diarize field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = writer.Close(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to close multipart writer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.endpoint + "/v1/speech-to-text"
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
|
|
||||||
|
res, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch res.StatusCode {
|
||||||
|
case 401:
|
||||||
|
return nil, ErrUnauthorized
|
||||||
|
case 200:
|
||||||
|
var sttResponse types.SpeechToTextResponse
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&sttResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse API response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sttResponse, nil
|
||||||
|
case 422:
|
||||||
|
ve := types.ValidationError{}
|
||||||
|
defer res.Body.Close()
|
||||||
|
jerr := json.NewDecoder(res.Body).Decode(&ve)
|
||||||
|
if jerr != nil {
|
||||||
|
err = errors.Join(err, jerr)
|
||||||
|
} else {
|
||||||
|
err = errors.Join(err, ve)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
case 400:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
ve := types.ParamError{}
|
||||||
|
defer res.Body.Close()
|
||||||
|
jerr := json.NewDecoder(res.Body).Decode(&ve)
|
||||||
|
if jerr != nil {
|
||||||
|
err = errors.Join(err, jerr)
|
||||||
|
} else {
|
||||||
|
err = errors.Join(err, ve)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
187
client/tts.go
187
client/tts.go
@@ -12,34 +12,100 @@ import (
|
|||||||
"github.com/taigrr/elevenlabs/client/types"
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c Client) TTSWriter(ctx context.Context, w io.Writer, text, modelID, voiceID string, options types.SynthesisOptions) error {
|
func WithPreviousText(previousText string) types.TTSParam {
|
||||||
options.Clamp()
|
return func(tts *types.TTS) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/text-to-speech/%s", voiceID)
|
tts.PreviousText = previousText
|
||||||
opts := types.TTS{
|
|
||||||
Text: text,
|
|
||||||
ModelID: modelID,
|
|
||||||
VoiceSettings: options,
|
|
||||||
}
|
}
|
||||||
b, _ := json.Marshal(opts)
|
}
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
|
func WithNextText(nextText string) types.TTSParam {
|
||||||
|
return func(tts *types.TTS) {
|
||||||
|
tts.NextText = nextText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) TTSWriter(ctx context.Context, w io.Writer, text, modelID, voiceID string, options types.SynthesisOptions, optionalParams ...types.TTSParam) error {
|
||||||
|
params := types.TTS{
|
||||||
|
Text: text,
|
||||||
|
VoiceID: voiceID,
|
||||||
|
ModelID: modelID,
|
||||||
|
}
|
||||||
|
for _, p := range optionalParams {
|
||||||
|
p(¶ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.requestTTS(ctx, params, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer body.Close()
|
||||||
|
io.Copy(w, body)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) TTS(ctx context.Context, text, voiceID, modelID string, options types.SynthesisOptions, optionalParams ...types.TTSParam) ([]byte, error) {
|
||||||
|
params := types.TTS{
|
||||||
|
Text: text,
|
||||||
|
VoiceID: voiceID,
|
||||||
|
ModelID: modelID,
|
||||||
|
}
|
||||||
|
for _, p := range optionalParams {
|
||||||
|
p(¶ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.requestTTS(ctx, params, options)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
io.Copy(&b, body)
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) TTSStream(ctx context.Context, w io.Writer, text, voiceID string, options types.SynthesisOptions, optionalParams ...types.TTSParam) error {
|
||||||
|
params := types.TTS{
|
||||||
|
Text: text,
|
||||||
|
VoiceID: voiceID,
|
||||||
|
Stream: true,
|
||||||
|
}
|
||||||
|
for _, p := range optionalParams {
|
||||||
|
p(¶ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.requestTTS(ctx, params, options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
io.Copy(w, body)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) requestTTS(ctx context.Context, params types.TTS, options types.SynthesisOptions) (io.ReadCloser, error) {
|
||||||
|
options.Clamp()
|
||||||
|
url := fmt.Sprintf(c.endpoint+"/v1/text-to-speech/%s", params.VoiceID)
|
||||||
|
if params.Stream {
|
||||||
|
url += "/stream"
|
||||||
|
}
|
||||||
|
b, _ := json.Marshal(params)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
req.Header.Set("accept", "audio/mpeg")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
return ErrUnauthorized
|
return nil, ErrUnauthorized
|
||||||
case 200:
|
case 200:
|
||||||
defer res.Body.Close()
|
return res.Body, nil
|
||||||
io.Copy(w, res.Body)
|
|
||||||
return nil
|
|
||||||
case 422:
|
case 422:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
@@ -51,93 +117,6 @@ func (c Client) TTSWriter(ctx context.Context, w io.Writer, text, modelID, voice
|
|||||||
} else {
|
} else {
|
||||||
err = errors.Join(err, ve)
|
err = errors.Join(err, ve)
|
||||||
}
|
}
|
||||||
return err
|
return nil, err
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Client) TTS(ctx context.Context, text, voiceID, modelID string, options types.SynthesisOptions) ([]byte, error) {
|
|
||||||
options.Clamp()
|
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/text-to-speech/%s", voiceID)
|
|
||||||
client := &http.Client{}
|
|
||||||
opts := types.TTS{
|
|
||||||
Text: text,
|
|
||||||
ModelID: modelID,
|
|
||||||
VoiceSettings: options,
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(opts)
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, err
|
|
||||||
}
|
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return []byte{}, err
|
|
||||||
}
|
|
||||||
switch res.StatusCode {
|
|
||||||
case 401:
|
|
||||||
return []byte{}, ErrUnauthorized
|
|
||||||
case 200:
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
|
|
||||||
defer res.Body.Close()
|
|
||||||
io.Copy(&b, res.Body)
|
|
||||||
return b.Bytes(), nil
|
|
||||||
case 422:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
ve := types.ValidationError{}
|
|
||||||
defer res.Body.Close()
|
|
||||||
jerr := json.NewDecoder(res.Body).Decode(&ve)
|
|
||||||
if jerr != nil {
|
|
||||||
err = errors.Join(err, jerr)
|
|
||||||
} else {
|
|
||||||
err = errors.Join(err, ve)
|
|
||||||
}
|
|
||||||
return []byte{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Client) TTSStream(ctx context.Context, w io.Writer, text, voiceID string, options types.SynthesisOptions) error {
|
|
||||||
options.Clamp()
|
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/text-to-speech/%s/stream", voiceID)
|
|
||||||
opts := types.TTS{
|
|
||||||
Text: text,
|
|
||||||
VoiceSettings: options,
|
|
||||||
}
|
|
||||||
b, _ := json.Marshal(opts)
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
|
||||||
req.Header.Set("accept", "audio/mpeg")
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch res.StatusCode {
|
|
||||||
case 401:
|
|
||||||
return ErrUnauthorized
|
|
||||||
case 200:
|
|
||||||
defer res.Body.Close()
|
|
||||||
io.Copy(w, res.Body)
|
|
||||||
return nil
|
|
||||||
case 422:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
ve := types.ValidationError{}
|
|
||||||
defer res.Body.Close()
|
|
||||||
jerr := json.NewDecoder(res.Body).Decode(&ve)
|
|
||||||
if jerr != nil {
|
|
||||||
err = errors.Join(err, jerr)
|
|
||||||
} else {
|
|
||||||
err = errors.Join(err, ve)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,17 @@ type Voice struct {
|
|||||||
Labels string `json:"labels,omitempty"` // Serialized labels dictionary for the voice.
|
Labels string `json:"labels,omitempty"` // Serialized labels dictionary for the voice.
|
||||||
}
|
}
|
||||||
type TTS struct {
|
type TTS struct {
|
||||||
|
VoiceID string `json:"voice_id"` // The ID of the voice that will be used to generate the speech.
|
||||||
ModelID string `json:"model_id,omitempty"`
|
ModelID string `json:"model_id,omitempty"`
|
||||||
Text string `json:"text"` // The text that will get converted into speech. Currently only English text is supported.
|
Text string `json:"text"` // The text that will get converted into speech.
|
||||||
|
PreviousText string `json:"previous_text,omitempty"` // The text that was used to generate the previous audio file.
|
||||||
|
NextText string `json:"next_text,omitempty"` // The text that will be used to generate the next audio file.
|
||||||
VoiceSettings SynthesisOptions `json:"voice_settings,omitempty"` // Voice settings are applied only on the given TTS request.
|
VoiceSettings SynthesisOptions `json:"voice_settings,omitempty"` // Voice settings are applied only on the given TTS request.
|
||||||
|
Stream bool `json:"stream,omitempty"` // If true, the response will be a stream of audio data.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TTSParam func(*TTS)
|
||||||
|
|
||||||
func (so *SynthesisOptions) Clamp() {
|
func (so *SynthesisOptions) Clamp() {
|
||||||
if so.Stability > 1 || so.Stability < 0 {
|
if so.Stability > 1 || so.Stability < 0 {
|
||||||
so.Stability = 0.75
|
so.Stability = 0.75
|
||||||
@@ -31,11 +37,34 @@ func (so *SynthesisOptions) Clamp() {
|
|||||||
if so.SimilarityBoost > 1 || so.SimilarityBoost < 0 {
|
if so.SimilarityBoost > 1 || so.SimilarityBoost < 0 {
|
||||||
so.SimilarityBoost = 0.75
|
so.SimilarityBoost = 0.75
|
||||||
}
|
}
|
||||||
|
if so.Style > 1 || so.Style < 0 {
|
||||||
|
so.Style = 0.0
|
||||||
|
}
|
||||||
|
// UseSpeakerBoost defaults to the zero value (false) for bool;
|
||||||
|
// no conditional needed as the caller sets it explicitly.
|
||||||
}
|
}
|
||||||
|
|
||||||
type SynthesisOptions struct {
|
type SynthesisOptions struct {
|
||||||
Stability float64 `json:"stability"`
|
Stability float64 `json:"stability"`
|
||||||
SimilarityBoost float64 `json:"similarity_boost"`
|
SimilarityBoost float64 `json:"similarity_boost"`
|
||||||
|
Style float64 `json:"style"`
|
||||||
|
UseSpeakerBoost bool `json:"use_speaker_boost"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharingOptions struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
HistoryItemSampleId string `json:"history_item_sample_id"`
|
||||||
|
OriginalVoiceId string `json:"original_voice_id"`
|
||||||
|
PublicOwnerId string `json:"public_owner_id"`
|
||||||
|
LikedByCount int32 `json:"liked_by_count"`
|
||||||
|
ClonedByCount int32 `json:"cloned_by_count"`
|
||||||
|
WhitelistedEmails []string `json:"whitelisted_emails"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Labels map[string]string `json:"labels"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ReviewStatus string `json:"review_status"`
|
||||||
|
ReviewMessage string `json:"review_message"`
|
||||||
|
EnabledInLibrary bool `json:"enabled_in_library"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtendedSubscriptionResponseModel struct {
|
type ExtendedSubscriptionResponseModel struct {
|
||||||
@@ -172,6 +201,17 @@ func (ve ValidationError) Error() string {
|
|||||||
return fmt.Sprintf("%s %s: ", ve.Type_, ve.Msg)
|
return fmt.Sprintf("%s %s: ", ve.Type_, ve.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ParamError struct {
|
||||||
|
Detail struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"detail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe ParamError) Error() string {
|
||||||
|
return fmt.Sprintf("%s %s: ", pe.Detail.Status, pe.Detail.Message)
|
||||||
|
}
|
||||||
|
|
||||||
type VerificationAttemptResponseModel struct {
|
type VerificationAttemptResponseModel struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
DateUnix int32 `json:"date_unix"`
|
DateUnix int32 `json:"date_unix"`
|
||||||
@@ -181,14 +221,95 @@ type VerificationAttemptResponseModel struct {
|
|||||||
Recording *RecordingResponseModel `json:"recording"`
|
Recording *RecordingResponseModel `json:"recording"`
|
||||||
}
|
}
|
||||||
type VoiceResponseModel struct {
|
type VoiceResponseModel struct {
|
||||||
VoiceID string `json:"voice_id"`
|
VoiceID string `json:"voice_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Samples []Sample `json:"samples"`
|
Samples []Sample `json:"samples"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
FineTuning FineTuningResponseModel `json:"fine_tuning"`
|
FineTuning FineTuningResponseModel `json:"fine_tuning"`
|
||||||
Labels map[string]string `json:"labels"`
|
Labels map[string]string `json:"labels"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
PreviewURL string `json:"preview_url"`
|
PreviewURL string `json:"preview_url"`
|
||||||
AvailableForTiers []string `json:"available_for_tiers"`
|
AvailableForTiers []string `json:"available_for_tiers"`
|
||||||
Settings SynthesisOptions `json:"settings"`
|
Settings SynthesisOptions `json:"settings"`
|
||||||
|
Sharing SharingOptions `json:"sharing"`
|
||||||
|
HighQualityBaseModelIds []string `json:"high_quality_base_model_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SoundGeneration struct {
|
||||||
|
Text string `json:"text"` // The text that will get converted into a sound effect.
|
||||||
|
DurationSeconds float64 `json:"duration_seconds"` // The duration of the sound which will be generated in seconds.
|
||||||
|
PromptInfluence float64 `json:"prompt_influence"` // A higher prompt influence makes your generation follow the prompt more closely.
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimestampsGranularity string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TimestampsGranularityNone represents no timestamps
|
||||||
|
TimestampsGranularityNone TimestampsGranularity = "none"
|
||||||
|
// TimestampsGranularityWord represents word-level timestamps
|
||||||
|
TimestampsGranularityWord TimestampsGranularity = "word"
|
||||||
|
// TimestampsGranularityCharacter represents character-level timestamps
|
||||||
|
TimestampsGranularityCharacter TimestampsGranularity = "character"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpeechToTextModel string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SpeechToTextModelScribeV1 SpeechToTextModel = "scribe_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SpeechToTextRequest represents a request to the speech-to-text API
|
||||||
|
type SpeechToTextRequest struct {
|
||||||
|
// The ID of the model to use for transcription (currently only 'scribe_v1')
|
||||||
|
ModelID SpeechToTextModel `json:"model_id"`
|
||||||
|
// ISO-639-1 or ISO-639-3 language code. If not specified, language is auto-detected
|
||||||
|
LanguageCode string `json:"language_code,omitempty"`
|
||||||
|
// Whether to tag audio events like (laughter), (footsteps), etc.
|
||||||
|
TagAudioEvents bool `json:"tag_audio_events,omitempty"`
|
||||||
|
// Number of speakers (1-32). If not specified, uses model's maximum supported
|
||||||
|
NumSpeakers int `json:"num_speakers,omitempty"`
|
||||||
|
// Granularity of timestamps: "none", "word", or "character"
|
||||||
|
TimestampsGranularity TimestampsGranularity `json:"timestamps_granularity,omitempty"`
|
||||||
|
// Whether to annotate speaker changes (limits input to 8 minutes)
|
||||||
|
Diarize bool `json:"diarize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpeechToTextResponse represents the response from the speech-to-text API
|
||||||
|
type SpeechToTextResponse struct {
|
||||||
|
// ISO-639-1 language code
|
||||||
|
LanguageCode string `json:"language_code"`
|
||||||
|
// The probability of the detected language
|
||||||
|
LanguageProbability float64 `json:"language_probability"`
|
||||||
|
// The transcribed text
|
||||||
|
Text string `json:"text"`
|
||||||
|
// Detailed word-level information
|
||||||
|
Words []TranscriptionWord `json:"words"`
|
||||||
|
// Error message, if any
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranscriptionWord represents a word or spacing in the transcription
|
||||||
|
type TranscriptionWord struct {
|
||||||
|
// The text content of the word/spacing
|
||||||
|
Text string `json:"text"`
|
||||||
|
// Type of segment ("word" or "spacing")
|
||||||
|
Type string `json:"type"`
|
||||||
|
// Start time in seconds
|
||||||
|
Start float64 `json:"start"`
|
||||||
|
// End time in seconds
|
||||||
|
End float64 `json:"end"`
|
||||||
|
// Speaker identifier for multi-speaker transcriptions
|
||||||
|
SpeakerID string `json:"speaker_id,omitempty"`
|
||||||
|
// Character-level information
|
||||||
|
Characters []TranscriptionCharacter `json:"characters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranscriptionCharacter represents character-level information in the transcription
|
||||||
|
type TranscriptionCharacter struct {
|
||||||
|
// The text content of the character
|
||||||
|
Text string `json:"text"`
|
||||||
|
// Start time in seconds
|
||||||
|
Start float64 `json:"start"`
|
||||||
|
// End time in seconds
|
||||||
|
End float64 `json:"end"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
func (c Client) GetUserInfo(ctx context.Context) (types.UserResponseModel, error) {
|
func (c Client) GetUserInfo(ctx context.Context) (types.UserResponseModel, error) {
|
||||||
url := c.endpoint + "/v1/user"
|
url := c.endpoint + "/v1/user"
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.UserResponseModel{}, err
|
return types.UserResponseModel{}, err
|
||||||
@@ -19,7 +18,7 @@ func (c Client) GetUserInfo(ctx context.Context) (types.UserResponseModel, error
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.UserResponseModel{}, err
|
return types.UserResponseModel{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import (
|
|||||||
|
|
||||||
func (c Client) CreateVoice(ctx context.Context, name, description string, labels []string, files []*os.File) error {
|
func (c Client) CreateVoice(ctx context.Context, name, description string, labels []string, files []*os.File) error {
|
||||||
url := c.endpoint + "/v1/voices/add"
|
url := c.endpoint + "/v1/voices/add"
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
w := multipart.NewWriter(&b)
|
w := multipart.NewWriter(&b)
|
||||||
for _, r := range files {
|
for _, r := range files {
|
||||||
@@ -43,7 +41,7 @@ func (c Client) CreateVoice(ctx context.Context, name, description string, label
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -69,7 +67,6 @@ func (c Client) CreateVoice(ctx context.Context, name, description string, label
|
|||||||
|
|
||||||
func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
|
func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -77,7 +74,7 @@ func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -103,7 +100,7 @@ func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
|
|||||||
|
|
||||||
func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings types.SynthesisOptions) error {
|
func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings types.SynthesisOptions) error {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings/edit", voiceID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings/edit", voiceID)
|
||||||
client := &http.Client{}
|
|
||||||
b, _ := json.Marshal(settings)
|
b, _ := json.Marshal(settings)
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b))
|
||||||
@@ -113,7 +110,7 @@ func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -145,7 +142,6 @@ func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings
|
|||||||
|
|
||||||
func (c Client) EditVoice(ctx context.Context, voiceID, name, description string, labels []string, files []*os.File) error {
|
func (c Client) EditVoice(ctx context.Context, voiceID, name, description string, labels []string, files []*os.File) error {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/edit", voiceID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/edit", voiceID)
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
w := multipart.NewWriter(&b)
|
w := multipart.NewWriter(&b)
|
||||||
@@ -171,7 +167,7 @@ func (c Client) EditVoice(ctx context.Context, voiceID, name, description string
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -195,49 +191,9 @@ func (c Client) EditVoice(ctx context.Context, voiceID, name, description string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) defaultVoiceSettings(ctx context.Context) (types.SynthesisOptions, error) {
|
|
||||||
url := c.endpoint + "/v1/voices/settings/default"
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.SynthesisOptions{}, err
|
|
||||||
}
|
|
||||||
req.Header.Set("xi-api-key", c.apiKey)
|
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
|
||||||
req.Header.Set("accept", "application/json")
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return types.SynthesisOptions{}, err
|
|
||||||
}
|
|
||||||
switch res.StatusCode {
|
|
||||||
case 401:
|
|
||||||
return types.SynthesisOptions{}, ErrUnauthorized
|
|
||||||
case 200:
|
|
||||||
so := types.SynthesisOptions{}
|
|
||||||
defer res.Body.Close()
|
|
||||||
jerr := json.NewDecoder(res.Body).Decode(&so)
|
|
||||||
if jerr != nil {
|
|
||||||
return types.SynthesisOptions{}, jerr
|
|
||||||
}
|
|
||||||
return so, nil
|
|
||||||
case 422:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
ve := types.ValidationError{}
|
|
||||||
defer res.Body.Close()
|
|
||||||
jerr := json.NewDecoder(res.Body).Decode(&ve)
|
|
||||||
if jerr != nil {
|
|
||||||
err = errors.Join(err, jerr)
|
|
||||||
} else {
|
|
||||||
err = errors.Join(err, ve)
|
|
||||||
}
|
|
||||||
return types.SynthesisOptions{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.SynthesisOptions, error) {
|
func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.SynthesisOptions, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings", voiceID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings", voiceID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.SynthesisOptions{}, err
|
return types.SynthesisOptions{}, err
|
||||||
@@ -245,7 +201,7 @@ func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.Syn
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.SynthesisOptions{}, err
|
return types.SynthesisOptions{}, err
|
||||||
}
|
}
|
||||||
@@ -277,7 +233,7 @@ func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.Syn
|
|||||||
|
|
||||||
func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceResponseModel, error) {
|
func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceResponseModel, error) {
|
||||||
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
|
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.VoiceResponseModel{}, err
|
return types.VoiceResponseModel{}, err
|
||||||
@@ -285,7 +241,7 @@ func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceRespon
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
return types.VoiceResponseModel{}, ErrUnauthorized
|
return types.VoiceResponseModel{}, ErrUnauthorized
|
||||||
@@ -319,7 +275,7 @@ func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceRespon
|
|||||||
|
|
||||||
func (c Client) GetVoices(ctx context.Context) ([]types.VoiceResponseModel, error) {
|
func (c Client) GetVoices(ctx context.Context) ([]types.VoiceResponseModel, error) {
|
||||||
url := c.endpoint + "/v1/voices"
|
url := c.endpoint + "/v1/voices"
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []types.VoiceResponseModel{}, err
|
return []types.VoiceResponseModel{}, err
|
||||||
@@ -327,7 +283,7 @@ func (c Client) GetVoices(ctx context.Context) ([]types.VoiceResponseModel, erro
|
|||||||
req.Header.Set("xi-api-key", c.apiKey)
|
req.Header.Set("xi-api-key", c.apiKey)
|
||||||
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
res, err := client.Do(req)
|
res, err := c.httpClient.Do(req)
|
||||||
switch res.StatusCode {
|
switch res.StatusCode {
|
||||||
case 401:
|
case 401:
|
||||||
return []types.VoiceResponseModel{}, ErrUnauthorized
|
return []types.VoiceResponseModel{}, ErrUnauthorized
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/faiface/beep"
|
"github.com/gopxl/beep/v2"
|
||||||
"github.com/faiface/beep/mp3"
|
"github.com/gopxl/beep/v2/mp3"
|
||||||
"github.com/faiface/beep/speaker"
|
"github.com/gopxl/beep/v2/speaker"
|
||||||
|
|
||||||
"github.com/taigrr/elevenlabs/client"
|
"github.com/taigrr/elevenlabs/client"
|
||||||
"github.com/taigrr/elevenlabs/client/types"
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
@@ -25,12 +26,23 @@ func main() {
|
|||||||
}
|
}
|
||||||
pipeReader, pipeWriter := io.Pipe()
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
// record how long it takes to run and print out on exit
|
||||||
b, _ := io.ReadAll(reader)
|
start := time.Now()
|
||||||
text := string(b)
|
defer func() {
|
||||||
|
log.Println(time.Since(start))
|
||||||
|
}()
|
||||||
|
|
||||||
|
var text string
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
text = strings.Join(os.Args[1:], " ")
|
||||||
|
} else {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
b, _ := io.ReadAll(reader)
|
||||||
|
text = string(b)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err = client.TTSStream(ctx, pipeWriter, text, ids[0], types.SynthesisOptions{Stability: 0.75, SimilarityBoost: 0.75})
|
err = client.TTSStream(ctx, pipeWriter, text, ids[0], types.SynthesisOptions{Stability: 0.75, SimilarityBoost: 0.75, Style: 0.0, UseSpeakerBoost: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
3
cmd/transcribe/.gitignore
vendored
Normal file
3
cmd/transcribe/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.mp3
|
||||||
|
main
|
||||||
|
transcribe
|
||||||
34
cmd/transcribe/main.go
Normal file
34
cmd/transcribe/main.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/taigrr/elevenlabs/client"
|
||||||
|
"github.com/taigrr/elevenlabs/client/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := client.New(os.Getenv("XI_API_KEY"))
|
||||||
|
|
||||||
|
filePath := os.Args[1]
|
||||||
|
|
||||||
|
resp, err := client.ConvertSpeechToText(ctx, filePath, types.SpeechToTextRequest{
|
||||||
|
ModelID: types.SpeechToTextModelScribeV1,
|
||||||
|
TimestampsGranularity: types.TimestampsGranularityWord,
|
||||||
|
Diarize: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(bytes))
|
||||||
|
}
|
||||||
14
go.mod
14
go.mod
@@ -1,15 +1,13 @@
|
|||||||
module github.com/taigrr/elevenlabs
|
module github.com/taigrr/elevenlabs
|
||||||
|
|
||||||
go 1.20
|
go 1.26.0
|
||||||
|
|
||||||
require github.com/faiface/beep v1.1.0
|
require github.com/gopxl/beep/v2 v2.1.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hajimehoshi/go-mp3 v0.3.0 // indirect
|
github.com/ebitengine/oto/v3 v3.4.0 // indirect
|
||||||
github.com/hajimehoshi/oto v0.7.1 // indirect
|
github.com/ebitengine/purego v0.10.0 // indirect
|
||||||
|
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 // indirect
|
|
||||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
56
go.sum
56
go.sum
@@ -1,38 +1,22 @@
|
|||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
|
github.com/ebitengine/oto/v3 v3.4.0 h1:br0PgASsEWaoWn38b2Goe7m1GKFYfNgnsjSd5Gg+/bQ=
|
||||||
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
|
github.com/ebitengine/oto/v3 v3.4.0/go.mod h1:IOleLVD0m+CMak3mRVwsYY8vTctQgOM0iiL6S7Ar7eI=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
|
||||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
|
||||||
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
|
||||||
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||||
github.com/hajimehoshi/go-mp3 v0.3.0 h1:fTM5DXjp/DL2G74HHAs/aBGiS9Tg7wnp+jkU38bHy4g=
|
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||||
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
|
||||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
|
||||||
github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk=
|
|
||||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
|
||||||
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
|
||||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
|
||||||
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
|
|
||||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
|
||||||
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
|
||||||
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc=
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
|
|||||||
Reference in New Issue
Block a user