Skip to content
Open
Show file tree
Hide file tree
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
44 changes: 44 additions & 0 deletions app/cognitive_profile.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package app

import (
"strings"

"github.com/webitel/engine/auth_manager"
engine "github.com/webitel/engine/model"
"github.com/webitel/storage/model"
tts2 "github.com/webitel/storage/tts"
)

type ttsVoiceFunction func(domainId int64, params *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError)

var (
ttsVoiceEngine = map[string]ttsVoiceFunction{
strings.ToLower(TtsMicrosoft): tts2.MicrosoftVoice,
strings.ToLower(TtsGoogle): tts2.GoogleVoice,
strings.ToLower(TtsElevenLabs): tts2.ElevenLabsVoice,
}
)

func (app *App) CognitiveProfileCheckAccess(domainId, id int64, groups []int, access auth_manager.PermissionAccess) (bool, engine.AppError) {
Expand Down Expand Up @@ -32,6 +45,37 @@ func (app *App) SearchCognitiveProfilesByGroups(domainId int64, groups []int, se
return res, search.EndOfList(), nil
}

func (app *App) SearchCognitiveProfileVoices(domainId int64, search *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError) {
var ttsProfile *model.TtsProfile
ttsProfile, err := app.Store.CognitiveProfile().SearchTtsProfile(domainId, int(search.Id))
if err != nil {
return nil, err
}
if !ttsProfile.Enabled {
err = engine.NewBadRequestError("tts.profile.disabled", "Profile is disabled")

return nil, err
}

provider := ttsProfile.Provider
provider = strings.ToLower(provider)

if fn, ok := ttsVoiceEngine[provider]; ok {
res, ttsErr := fn(domainId, search)
if ttsErr != nil {
switch ttsErr.(type) {
case engine.AppError:
return nil, engine.NewNotFoundError("tts.valid.not_found", "Not found provider")
default:
return nil, engine.NewInternalError("tts.app_error", ttsErr.Error())
}
}
return res, nil
}

return nil, engine.NewNotFoundError("tts.valid.not_found", "Not found provider")
}

func (app *App) GetCognitiveProfile(id, domain int64) (*model.CognitiveProfile, engine.AppError) {
return app.Store.CognitiveProfile().Get(id, domain)
}
Expand Down
14 changes: 14 additions & 0 deletions controller/cognitive_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ func (c *Controller) SearchCognitiveProfile(session *auth_manager.Session, domai
return list, endOfList, err
}

func (c *Controller) SearchCognitiveProfileVoice(session *auth_manager.Session, domainId int64, search *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError) {
permission := session.GetPermission(model.PermissionScopeCognitiveProfile)
if !permission.CanRead() {
return nil, c.app.MakePermissionError(session, permission, auth_manager.PERMISSION_ACCESS_READ)
}

var list []*model.CognitiveProfileVoice
var err engine.AppError

list, err = c.app.SearchCognitiveProfileVoices(session.Domain(domainId), search)

return list, err
}

func (c *Controller) GetCognitiveProfile(session *auth_manager.Session, id int64, domainId int64) (*model.CognitiveProfile, engine.AppError) {
var err engine.AppError
permission := session.GetPermission(model.PermissionScopeCognitiveProfile)
Expand Down
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.19

require (
buf.build/gen/go/webitel/engine/protocolbuffers/go v1.34.2-20240402125447-cb375844242f.2
buf.build/gen/go/webitel/storage/grpc/go v1.4.0-20240731055617-28b8bce074fa.2
buf.build/gen/go/webitel/storage/protocolbuffers/go v1.34.2-20240731055617-28b8bce074fa.2
buf.build/gen/go/webitel/storage/grpc/go v1.5.1-20241001075334-99db0a72402e.1
buf.build/gen/go/webitel/storage/protocolbuffers/go v1.34.2-20241001075334-99db0a72402e.2
cloud.google.com/go/speech v1.23.1
cloud.google.com/go/storage v1.39.1
cloud.google.com/go/texttospeech v1.7.7
Expand All @@ -28,7 +28,7 @@ require (
golang.org/x/sync v0.7.0
google.golang.org/api v0.177.0
google.golang.org/genproto v0.0.0-20240506185236-b8a5c65736ae
google.golang.org/grpc v1.63.2
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.34.2
)

Expand Down Expand Up @@ -75,12 +75,12 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.19.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
Expand Down
38 changes: 38 additions & 0 deletions grpc_api/cognitive_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,37 @@ func (api *cognitiveProfile) DeleteCognitiveProfile(ctx context.Context, in *sto
return toGrpcCognitiveProfile(profile), nil
}

func (api *cognitiveProfile) SearchCognitiveProfileVoices(ctx context.Context, in *storage.SearchCognitiveProfileVoicesRequest) (*storage.ListCognitiveProfileVoices, error) {
session, err := api.ctrl.GetSessionFromCtx(ctx)
if err != nil {
return nil, err
}

var list []*model.CognitiveProfileVoice

rec := &model.SearchCognitiveProfileVoice{
ListRequest: model.ListRequest{
Q: in.GetQ(),
},
Id: in.GetId(),
Key: in.Key,
}

list, err = api.ctrl.SearchCognitiveProfileVoice(session, session.Domain(0), rec)

if err != nil {
return nil, err
}

items := make([]*storage.CognitiveProfileVoice, 0, len(list))
for _, v := range list {
items = append(items, toGrpcCognitiveProfileVoice(v))
}
return &storage.ListCognitiveProfileVoices{
Items: items,
}, nil
}

func toGrpcCognitiveProfile(src *model.CognitiveProfile) *storage.CognitiveProfile {
// nullify password
src.Properties.Remove(model.CognitiveProfileKeyField)
Expand All @@ -207,6 +238,13 @@ func toGrpcCognitiveProfile(src *model.CognitiveProfile) *storage.CognitiveProfi
}
}

func toGrpcCognitiveProfileVoice(src *model.CognitiveProfileVoice) *storage.CognitiveProfileVoice {
return &storage.CognitiveProfileVoice{
Id: src.Id,
Name: src.Name,
}
}

func getProvider(p string) storage.ProviderType {
switch p {
case storage.ProviderType_Microsoft.String():
Expand Down
11 changes: 11 additions & 0 deletions model/cognitive_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ type CognitiveProfile struct {
SyncTag int64 `json:"-" db:"-"`
}

type CognitiveProfileVoice struct {
Id string `json:"id" db:"id"`
Name string `json:"name" db:"name"`
}

type SearchCognitiveProfileVoice struct {
ListRequest
Id int64
Key string
}

type SearchCognitiveProfile struct {
ListRequest
Ids []int64
Expand Down
65 changes: 65 additions & 0 deletions tts/elevenlabs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tts

import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
Expand All @@ -9,6 +10,9 @@ import (
"net/http"
"strconv"
"strings"

engine "github.com/webitel/engine/model"
"github.com/webitel/storage/model"
)

type ElevenLabsVoiceSettings struct {
Expand All @@ -24,6 +28,16 @@ type ElevenLabsRequest struct {
VoiceSettings ElevenLabsVoiceSettings `json:"voice_settings"`
}

type Voice struct {
VoiceID string `json:"voice_id"`
Name string `json:"name"`
Category string `json:"category"`
}

type Response struct {
Voices []Voice `json:"voices"`
}

func ElevenLabs(params TTSParams) (io.ReadCloser, *string, *int, error) {
token := string(fixKey(params.Key))
voiceId := ""
Expand Down Expand Up @@ -93,3 +107,54 @@ func ElevenLabs(params TTSParams) (io.ReadCloser, *string, *int, error) {

return res.Body, &ct, nil, nil
}

func ElevenLabsVoice(domainId int64, req *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError) {
token := req.Key
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
var url string
if req.Q != "" {
url = fmt.Sprintf("https://api.elevenlabs.io/v1/shared-voices?search=%s", req.Q)
} else {
url = "https://api.elevenlabs.io/v1/voices"
}

resp, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, engine.NewCustomCodeError("store.cognitive_profile_store.search_voice.app_error", err.Error(), http.StatusInternalServerError)
}

resp.Header.Add("xi-api-key", token)

res, err := client.Do(resp)
if err != nil {
return nil, engine.NewCustomCodeError("store.cognitive_profile_store.search_voice.app_error", err.Error(), http.StatusInternalServerError)
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, engine.NewCustomCodeError("store.cognitive_profile_store.search_voice.app_error", err.Error(), http.StatusInternalServerError)
}

var response Response
err = json.Unmarshal(body, &response)
if err != nil {
return nil, engine.NewCustomCodeError("store.cognitive_profile_store.search_voice.app_error", err.Error(), http.StatusInternalServerError)
}

var filteredVoices []*model.CognitiveProfileVoice
for _, voice := range response.Voices {
if req.Q != "" || voice.Category == "generated" {
filteredVoices = append(filteredVoices, &model.CognitiveProfileVoice{
Id: voice.VoiceID,
Name: voice.Name,
})
}
}

return filteredVoices, nil
}
20 changes: 20 additions & 0 deletions tts/google.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"

texttospeech "cloud.google.com/go/texttospeech/apiv1"
engine "github.com/webitel/engine/model"
"github.com/webitel/storage/model"
"google.golang.org/api/option"
texttospeechpb "google.golang.org/genproto/googleapis/cloud/texttospeech/v1"
)
Expand Down Expand Up @@ -108,3 +110,21 @@ func Google(params TTSParams) (io.ReadCloser, *string, *int, error) {

return r, &v, &size, nil
}

func GoogleVoice(domainId int64, req *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError) {
var voices []*model.CognitiveProfileVoice
voices = append(voices, &model.CognitiveProfileVoice{
Id: "FEMALE",
Name: "FEMALE",
})
voices = append(voices, &model.CognitiveProfileVoice{
Id: "MALE",
Name: "MALE",
})
voices = append(voices, &model.CognitiveProfileVoice{
Id: "NEUTRAL",
Name: "NEUTRAL",
})

return voices, nil
}
15 changes: 15 additions & 0 deletions tts/microsoft.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

engine "github.com/webitel/engine/model"
"github.com/webitel/storage/model"
"github.com/webitel/wlog"
)

Expand Down Expand Up @@ -72,6 +73,20 @@ func Microsoft(req TTSParams) (io.ReadCloser, *string, *int, error) {
return result.Body, &contentType, nil, nil
}

func MicrosoftVoice(domainId int64, req *model.SearchCognitiveProfileVoice) ([]*model.CognitiveProfileVoice, engine.AppError) {
var voices []*model.CognitiveProfileVoice
voices = append(voices, &model.CognitiveProfileVoice{
Id: "FEMALE",
Name: "FEMALE",
})
voices = append(voices, &model.CognitiveProfileVoice{
Id: "MALE",
Name: "MALE",
})

return voices, nil
}

func microsoftToken(key, region string) (string, error) {
req, err := http.NewRequest("POST", fmt.Sprintf("https://%s.api.cognitive.microsoft.com/sts/v1.0/issueToken", region), nil)
if err != nil {
Expand Down