Skip to content

Commit b0965be

Browse files
committed
feat(server): add ManifestProvider & TtsProvider. enhance TEN-framework#72
1 parent 2a61131 commit b0965be

File tree

10 files changed

+362
-185
lines changed

10 files changed

+362
-185
lines changed

server/internal/http_server.go

+19-18
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,41 @@ import (
2121
)
2222

2323
type HttpServer struct {
24-
config HttpServerConfig
24+
deps HttpServerDepends
2525
server *http.Server
2626
}
2727

28+
type HttpServerDepends struct {
29+
Config HttpServerConfig
30+
MainSvc *service.MainService
31+
}
32+
2833
type HttpServerConfig struct {
2934
Address string
30-
31-
service.MainServiceConfig
3235
}
3336

3437
var (
3538
logTag = slog.String("service", "HTTP_SERVER")
3639
)
3740

38-
func NewHttpServer(httpServerConfig HttpServerConfig) *HttpServer {
39-
return &HttpServer{
40-
config: httpServerConfig,
41-
}
42-
}
43-
44-
func (s *HttpServer) Run() error {
41+
func NewHttpServer(deps HttpServerDepends) *HttpServer {
4542
r := gin.Default()
4643
r.Use(corsMiddleware())
4744

48-
mainSvc := service.NewMainService(s.config.MainServiceConfig)
49-
go mainSvc.CleanWorker()
50-
router.Apply(r, mainSvc)
51-
52-
slog.Info("server start", "address", s.config.Address, logTag)
45+
router.Apply(r, deps.MainSvc)
5346

54-
s.server = &http.Server{
55-
Addr: s.config.Address,
56-
Handler: r,
47+
return &HttpServer{
48+
deps: deps,
49+
server: &http.Server{
50+
Addr: deps.Config.Address,
51+
Handler: r,
52+
},
5753
}
54+
}
55+
56+
func (s *HttpServer) Run() error {
57+
slog.Info("server start", "address", s.server.Addr, logTag)
58+
go s.deps.MainSvc.CleanWorker()
5859
return s.server.ListenAndServe()
5960
}
6061

server/internal/provider/manifest.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package provider
2+
3+
import (
4+
"log/slog"
5+
"os"
6+
"path/filepath"
7+
"regexp"
8+
9+
"github.com/tidwall/sjson"
10+
)
11+
12+
type ManifestProvider struct {
13+
// manifestJsons
14+
// key: fileName `manifest.json` `manifest.elevenlabs.json`
15+
// value: manifestJson
16+
manifestJsons map[string]string
17+
}
18+
19+
func NewManifestProvider() *ManifestProvider {
20+
return &ManifestProvider{
21+
manifestJsons: make(map[string]string),
22+
}
23+
}
24+
25+
func (p *ManifestProvider) LoadManifest(manifestJsonDir string) error {
26+
files, err := os.ReadDir(manifestJsonDir)
27+
if err != nil {
28+
slog.Error("read manifestJsonDir failed", "err", err, "manifestJsonDir", manifestJsonDir)
29+
return err
30+
}
31+
32+
matcher := regexp.MustCompile(`^manifest(\..+)?\.json$`)
33+
for _, file := range files {
34+
if file.IsDir() {
35+
continue
36+
}
37+
if !matcher.MatchString(file.Name()) {
38+
continue
39+
}
40+
41+
filePath := filepath.Join(manifestJsonDir, file.Name())
42+
content, err := os.ReadFile(filePath)
43+
if err != nil {
44+
slog.Error("read manifest.json failed", "err", err, "filePath", filePath)
45+
return err
46+
}
47+
48+
manifestJson := string(content)
49+
manifestJson = p.injectEnvVar(manifestJson)
50+
51+
p.manifestJsons[file.Name()] = manifestJson
52+
}
53+
54+
return nil
55+
}
56+
57+
func (p *ManifestProvider) injectEnvVar(manifestJson string) string {
58+
appId := os.Getenv("AGORA_APP_ID")
59+
if appId != "" {
60+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.app_id`, appId)
61+
}
62+
63+
azureSttKey := os.Getenv("AZURE_STT_KEY")
64+
if azureSttKey != "" {
65+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.agora_asr_vendor_key`, azureSttKey)
66+
}
67+
68+
azureSttRegion := os.Getenv("AZURE_STT_REGION")
69+
if azureSttRegion != "" {
70+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.agora_asr_vendor_region`, azureSttRegion)
71+
}
72+
73+
openaiBaseUrl := os.Getenv("OPENAI_BASE_URL")
74+
if openaiBaseUrl != "" {
75+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="openai_chatgpt").property.base_url`, openaiBaseUrl)
76+
}
77+
78+
openaiApiKey := os.Getenv("OPENAI_API_KEY")
79+
if openaiApiKey != "" {
80+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="openai_chatgpt").property.api_key`, openaiApiKey)
81+
}
82+
83+
openaiModel := os.Getenv("OPENAI_MODEL")
84+
if openaiModel != "" {
85+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="openai_chatgpt").property.model`, openaiModel)
86+
}
87+
88+
proxyUrl := os.Getenv("PROXY_URL")
89+
if proxyUrl != "" {
90+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="openai_chatgpt").property.proxy_url`, proxyUrl)
91+
}
92+
93+
azureTtsKey := os.Getenv("AZURE_TTS_KEY")
94+
if azureTtsKey != "" {
95+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="azure_tts").property.azure_subscription_key`, azureTtsKey)
96+
}
97+
98+
azureTtsRegion := os.Getenv("AZURE_TTS_REGION")
99+
if azureTtsRegion != "" {
100+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="azure_tts").property.azure_subscription_region`, azureTtsRegion)
101+
}
102+
103+
elevenlabsTtsKey := os.Getenv("ELEVENLABS_TTS_KEY")
104+
if elevenlabsTtsKey != "" {
105+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="elevenlabs_tts").property.api_key`, elevenlabsTtsKey)
106+
}
107+
108+
return manifestJson
109+
}
110+
111+
func (p *ManifestProvider) GetManifestJson(vendor string) (string, bool) {
112+
if len(vendor) > 0 {
113+
vendor = "." + vendor
114+
}
115+
manifestJson, ok := p.manifestJsons["manifest"+vendor+".json"]
116+
return manifestJson, ok
117+
}

server/internal/service/service.go

+46-74
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package service
22

33
import (
4-
"app/internal/common"
4+
"app/internal/provider"
5+
"app/pkg/common"
6+
pkgProvider "app/pkg/provider"
7+
"errors"
58
"fmt"
69
"log/slog"
710
"net/http"
@@ -22,62 +25,34 @@ import (
2225
const (
2326
privilegeExpirationInSeconds = uint32(86400)
2427
tokenExpirationInSeconds = uint32(86400)
25-
26-
languageChinese = "zh-CN"
27-
languageEnglish = "en-US"
28-
29-
TTSVendorAzure = "azure"
30-
TTSVendorElevenlabs = "elevenlabs"
31-
32-
voiceTypeMale = "male"
33-
voiceTypeFemale = "female"
3428
)
3529

3630
var (
37-
voiceNameMap = map[string]map[string]map[string]string{
38-
languageChinese: {
39-
TTSVendorAzure: {
40-
voiceTypeMale: "zh-CN-YunxiNeural",
41-
voiceTypeFemale: "zh-CN-XiaoxiaoNeural",
42-
},
43-
TTSVendorElevenlabs: {
44-
voiceTypeMale: "pNInz6obpgDQGcFmaJgB", // Adam
45-
voiceTypeFemale: "Xb7hH8MSUJpSbSDYk0k2", // Alice
46-
},
47-
},
48-
languageEnglish: {
49-
TTSVendorAzure: {
50-
voiceTypeMale: "en-US-BrianNeural",
51-
voiceTypeFemale: "en-US-JaneNeural",
52-
},
53-
TTSVendorElevenlabs: {
54-
voiceTypeMale: "pNInz6obpgDQGcFmaJgB", // Adam
55-
voiceTypeFemale: "Xb7hH8MSUJpSbSDYk0k2", // Alice
56-
},
57-
},
58-
}
5931
logTag = slog.String("service", "MAIN_SERVICE")
6032
)
6133

6234
type MainService struct {
63-
config MainServiceConfig
35+
deps MainServiceDepends
6436
workers *gmap.Map
6537
}
6638

39+
type MainServiceDepends struct {
40+
Config MainServiceConfig
41+
ManifestProvider *provider.ManifestProvider
42+
}
43+
6744
type MainServiceConfig struct {
6845
AppId string
6946
AppCertificate string
70-
ManifestJson string
71-
ManifestJsonElevenlabs string
7247
TTSVendorChinese string
7348
TTSVendorEnglish string
7449
WorkersMax int
7550
WorkerQuitTimeoutSeconds int
7651
}
7752

78-
func NewMainService(config MainServiceConfig) *MainService {
53+
func NewMainService(deps MainServiceDepends) *MainService {
7954
return &MainService{
80-
config: config,
55+
deps: deps,
8156
workers: gmap.New(true),
8257
}
8358
}
@@ -145,8 +120,8 @@ func (s *MainService) HandlerStart(c *gin.Context) {
145120
return
146121
}
147122

148-
if workersRunning >= s.config.WorkersMax {
149-
slog.Error("handlerStart workers exceed", "workersRunning", workersRunning, "WorkersMax", s.config.WorkersMax, "requestId", req.RequestId, logTag)
123+
if workersRunning >= s.deps.Config.WorkersMax {
124+
slog.Error("handlerStart workers exceed", "workersRunning", workersRunning, "WorkersMax", s.deps.Config.WorkersMax, "requestId", req.RequestId, logTag)
150125
s.output(c, common.CodeErrWorkersLimit, http.StatusTooManyRequests)
151126
return
152127
}
@@ -165,7 +140,7 @@ func (s *MainService) HandlerStart(c *gin.Context) {
165140
}
166141

167142
worker := newWorker(req.ChannelName, logFile, manifestJsonFile)
168-
worker.QuitTimeoutSeconds = s.config.WorkerQuitTimeoutSeconds
143+
worker.QuitTimeoutSeconds = s.deps.Config.WorkerQuitTimeoutSeconds
169144
if err := worker.start(&req); err != nil {
170145
slog.Error("handlerStart start worker failed", "err", err, "requestId", req.RequestId, logTag)
171146
s.output(c, common.CodeErrStartWorkerFailed, http.StatusInternalServerError)
@@ -229,35 +204,48 @@ func (s *MainService) HandlerGenerateToken(c *gin.Context) {
229204
return
230205
}
231206

232-
if s.config.AppCertificate == "" {
233-
s.output(c, common.CodeSuccess, map[string]any{"appId": s.config.AppId, "token": s.config.AppId, "channel_name": req.ChannelName, "uid": req.Uid})
207+
if s.deps.Config.AppCertificate == "" {
208+
s.output(c, common.CodeSuccess, map[string]any{"appId": s.deps.Config.AppId, "token": s.deps.Config.AppId, "channel_name": req.ChannelName, "uid": req.Uid})
234209
return
235210
}
236211

237-
token, err := rtctokenbuilder.BuildTokenWithUid(s.config.AppId, s.config.AppCertificate, req.ChannelName, req.Uid, rtctokenbuilder.RolePublisher, tokenExpirationInSeconds, privilegeExpirationInSeconds)
212+
token, err := rtctokenbuilder.BuildTokenWithUid(s.deps.Config.AppId, s.deps.Config.AppCertificate, req.ChannelName, req.Uid, rtctokenbuilder.RolePublisher, tokenExpirationInSeconds, privilegeExpirationInSeconds)
238213
if err != nil {
239214
slog.Error("handlerGenerateToken generate token failed", "err", err, "requestId", req.RequestId, logTag)
240215
s.output(c, common.CodeErrGenerateTokenFailed, http.StatusBadRequest)
241216
return
242217
}
243218

244219
slog.Info("handlerGenerateToken end", "requestId", req.RequestId, logTag)
245-
s.output(c, common.CodeSuccess, map[string]any{"appId": s.config.AppId, "token": token, "channel_name": req.ChannelName, "uid": req.Uid})
220+
s.output(c, common.CodeSuccess, map[string]any{"appId": s.deps.Config.AppId, "token": token, "channel_name": req.ChannelName, "uid": req.Uid})
246221
}
247222

248223
// createWorkerManifest create worker temporary Mainfest.
249224
func (s *MainService) createWorkerManifest(req *common.StartReq) (manifestJsonFile string, logFile string, err error) {
250-
manifestJson := s.getManifestJson(req.AgoraAsrLanguage)
225+
vendor := s.getTtsVendor(req.AgoraAsrLanguage)
226+
tts := pkgProvider.GetTts(vendor)
227+
if tts == nil {
228+
err = errors.New(fmt.Sprintf("unknow tts vendor", vendor))
229+
slog.Error("handlerStart generate token failed", "err", err, "requestId", req.RequestId, logTag)
230+
return "", "", err
231+
}
232+
233+
manifestJson, ok := s.deps.ManifestProvider.GetManifestJson(vendor)
234+
if !ok {
235+
err = errors.New(fmt.Sprintf("unknow manifest vendor", vendor))
236+
slog.Error("handlerStart get manifest json failed", "err", err, "requestId", req.RequestId, logTag)
237+
return "", "", err
238+
}
251239

252-
if s.config.AppId != "" {
253-
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.app_id`, s.config.AppId)
240+
if s.deps.Config.AppId != "" {
241+
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.app_id`, s.deps.Config.AppId)
254242
}
255243
appId := gjson.Get(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.app_id`).String()
256244

257245
// Generate token
258246
token := appId
259-
if s.config.AppCertificate != "" {
260-
token, err = rtctokenbuilder.BuildTokenWithUid(appId, s.config.AppCertificate, req.ChannelName, 0, rtctokenbuilder.RoleSubscriber, tokenExpirationInSeconds, privilegeExpirationInSeconds)
247+
if s.deps.Config.AppCertificate != "" {
248+
token, err = rtctokenbuilder.BuildTokenWithUid(appId, s.deps.Config.AppCertificate, req.ChannelName, 0, rtctokenbuilder.RoleSubscriber, tokenExpirationInSeconds, privilegeExpirationInSeconds)
261249
if err != nil {
262250
slog.Error("handlerStart generate token failed", "err", err, "requestId", req.RequestId, logTag)
263251
return "", "", err
@@ -275,16 +263,11 @@ func (s *MainService) createWorkerManifest(req *common.StartReq) (manifestJsonFi
275263
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.remote_stream_id`, req.RemoteStreamId)
276264
}
277265

278-
language := gjson.Get(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.agora_asr_language`).String()
279-
280-
ttsVendor := s.getTtsVendor(language)
281-
voiceName := voiceNameMap[language][ttsVendor][req.VoiceType]
282-
if voiceName != "" {
283-
if ttsVendor == TTSVendorAzure {
284-
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="azure_tts").property.azure_synthesis_voice_name`, voiceName)
285-
} else if ttsVendor == TTSVendorElevenlabs {
286-
manifestJson, _ = sjson.Set(manifestJson, `predefined_graphs.0.nodes.#(name=="elevenlabs_tts").property.voice_id`, voiceName)
287-
}
266+
language := gjson.Get(manifestJson, `predefined_graphs.0.nodes.#(name=="agora_rtc").property.agora_asr_language`).String() //TODO check is correct? not req.AgoraAsrLanguage?
267+
manifestJson, err = tts.ProcessManifest(manifestJson, common.Language(language), req.VoiceType)
268+
if err != nil {
269+
slog.Error("handlerStart tts ProcessManifest failed", "err", err, "requestId", req.RequestId, logTag)
270+
return "", "", err
288271
}
289272

290273
channelNameMd5 := gmd5.MustEncryptString(req.ChannelName)
@@ -322,21 +305,10 @@ func (s *MainService) CleanWorker() {
322305
}
323306
}
324307

325-
func (s *MainService) getManifestJson(language string) (manifestJson string) {
326-
ttsVendor := s.getTtsVendor(language)
327-
manifestJson = s.config.ManifestJson
328-
329-
if ttsVendor == TTSVendorElevenlabs {
330-
manifestJson = s.config.ManifestJsonElevenlabs
331-
}
332-
333-
return manifestJson
334-
}
335-
336-
func (s *MainService) getTtsVendor(language string) string {
337-
if language == languageChinese {
338-
return s.config.TTSVendorChinese
308+
func (s *MainService) getTtsVendor(language common.Language) string {
309+
if language == common.LanguageChinese {
310+
return s.deps.Config.TTSVendorChinese
339311
}
340312

341-
return s.config.TTSVendorEnglish
313+
return s.deps.Config.TTSVendorEnglish
342314
}

server/internal/service/worker.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package service
22

33
import (
4-
"app/internal/common"
4+
"app/pkg/common"
55
"fmt"
66
"log/slog"
77
"os/exec"

0 commit comments

Comments
 (0)