chenyuanyang vor 6 Jahren
Ursprung
Commit
c34abb22b5

+ 11
- 0
.gitignore Datei anzeigen

@@ -0,0 +1,11 @@
1
+*.DS_Store
2
+*.log
3
+*.zip
4
+.idea
5
+.log
6
+.env
7
+*.swp
8
+*.swo
9
+database/
10
+tests/
11
+vendor/

+ 53
- 0
cmd/http/handler/index.go Datei anzeigen

@@ -0,0 +1,53 @@
1
+package handler
2
+
3
+import (
4
+	"git.links123.net/links123.com/pkg/request"
5
+	"git.links123.net/Slate/CorpusAI/service"
6
+	"git.links123.net/Slate/CorpusAI/service/api"
7
+	"github.com/Unknwon/i18n"
8
+	"github.com/gin-gonic/gin"
9
+	"errors"
10
+	"git.links123.net/Slate/CorpusAI/config"
11
+)
12
+
13
+func Healthy(ctx *gin.Context) {
14
+	cp := &api.CommonParams{}
15
+	if request.ParseParamFail(ctx, cp) {
16
+		return
17
+	}
18
+
19
+	request.Success(ctx, gin.H{"msg": i18n.Tr(cp.TargetStr, "hi", "Paul")})
20
+}
21
+
22
+// TextToSpeech godoc
23
+// @Tags 语料库
24
+// @Summary 获取音频接口base url
25
+// @Description 获取音频接口base url(主要用于海内外接口地址分开)
26
+// @Accept  json
27
+// @Produce  json
28
+// @Param ip query string false "IP地址, 默认无需传, 自动获取"
29
+// @Success 200 {string} json "{"audio_base_url": "https://campusai.links123.com/v1/en/tts","country_code": "国家code","ip": "ip地址"}"
30
+// @Failure 400 {string} json "{"msg": "error info"}"
31
+// @Failure 500 {string} json "{"msg": "error info"}"
32
+// @Router /en/audio_base_url [get]
33
+func GetAudioBaseUrl(ctx *gin.Context) {
34
+
35
+	ip := ctx.DefaultQuery("ip", ctx.ClientIP())
36
+
37
+	if ip == "" {
38
+		request.Fail(ctx, 400, errors.New("client ip error"))
39
+		return
40
+	}
41
+
42
+	countryCode := service.Ip2Country(ip)
43
+
44
+	appConfig := config.C.App
45
+
46
+	baseUrl := appConfig.CNHost + "/v1/en/tts"
47
+	if countryCode!="" && countryCode != "CN" {
48
+
49
+		baseUrl = appConfig.HKHost + "/v1/en/tts"
50
+	}
51
+
52
+	request.Success(ctx, gin.H{"audio_base_url": baseUrl,"country_code":countryCode,"ip":ip})
53
+}

+ 37
- 0
cmd/http/handler/speech_to_text.go Datei anzeigen

@@ -0,0 +1,37 @@
1
+package handler
2
+
3
+import (
4
+	"net/http"
5
+	"git.links123.net/links123.com/pkg/request"
6
+	"git.links123.net/Slate/CorpusAI/service"
7
+	"github.com/gin-gonic/gin"
8
+)
9
+// SpeechToText godoc
10
+// @Tags 语料库
11
+// @Summary 语音转文本
12
+// @Description speech to Text
13
+// @Accept  json
14
+// @Produce  json
15
+// @Param file query file true "发音文件"
16
+// @Param language path string false "语言,en英文,zh中文,默认英文"
17
+// @Param rate path int false "码率,8/16 默认16"
18
+// @Success 200 {string} string "test"
19
+// @Failure 400 {string} json "{"msg": "error info"}"
20
+// @Failure 500 {string} json "{"msg": "error info"}"
21
+// @Router /en/stt [post]
22
+func SpeechToText(ctx *gin.Context){
23
+	file, _, err := ctx.Request.FormFile("file")
24
+	if request.Fail(ctx,http.StatusBadRequest,err){
25
+		return
26
+	}
27
+
28
+	lang,_ := ctx.GetQuery("language")
29
+	rateStr,_ := ctx.GetQuery("rate")
30
+	
31
+	ret, err := service.SpeechToText(file,lang,rateStr)
32
+	if request.Fail(ctx,http.StatusInternalServerError,err) {
33
+		return
34
+	}
35
+
36
+	request.Success(ctx,ret)
37
+}

+ 161
- 0
cmd/http/handler/text_to_speech.go Datei anzeigen

@@ -0,0 +1,161 @@
1
+package handler
2
+
3
+import (
4
+	"errors"
5
+	"fmt"
6
+	"git.links123.net/Slate/CorpusAI/config"
7
+	"git.links123.net/Slate/CorpusAI/service"
8
+	"git.links123.net/Slate/CorpusAI/service/store/cache"
9
+	"git.links123.net/Slate/CorpusAI/service/store/mysql"
10
+	"git.links123.net/links123.com/pkg/request"
11
+	"github.com/gin-gonic/gin"
12
+	"net/http"
13
+	"net/url"
14
+	"strconv"
15
+	"strings"
16
+)
17
+
18
+// TextToSpeech godoc
19
+// @Tags 语料库
20
+// @Summary 文本转语音
21
+// @Description Text to speech
22
+// @Accept  json
23
+// @Produce  json
24
+// @Param text query string false "单词/短语/句子"
25
+// @Param languageCode query string false "en-US(美音)/en-GB(英音)" Enums(en-US, en-GB)
26
+// @Param gender query string false "MALE(男音)/FEMALE(女音)" Enums(MALE, FEMALE)
27
+// @Success 302 {string} json "https://lnks123-campus-tts-hk.oss-cn-hongkong.aliyuncs.com/xxxx.mp3"
28
+// @Failure 400 {string} json "{"msg": "error info"}"
29
+// @Failure 500 {string} json "{"msg": "error info"}"
30
+// @Router /en/tts [get]
31
+func TextToSpeechMain(ctx *gin.Context) {
32
+
33
+	//Text string, LanguageCode, Gender string
34
+	//voiceName := ctx.Query("voiceName")
35
+	speed, err := strconv.ParseFloat(ctx.DefaultQuery("speed", "1.00"), 64)
36
+	pitch, err := strconv.ParseFloat(ctx.DefaultQuery("pitch", "0.00"), 64)
37
+	// 获取参数内容,没有则返回空字符串
38
+	text, err := url.QueryUnescape(ctx.Query("text"))
39
+	languageCode, err := url.QueryUnescape(ctx.Query("languageCode"))
40
+	gender, err := url.QueryUnescape(ctx.Query("gender"))
41
+	voiceName := GetDefaultVoiceName(languageCode, gender)
42
+
43
+	if err != nil || voiceName == "" || text == "" {
44
+		request.Fail(ctx, 400, errors.New("text or  voiceName error"))
45
+		return
46
+	}
47
+
48
+	text = strings.Trim(text , "")
49
+
50
+	ossObjectKey := service.GetTtsOssKey(text, voiceName, languageCode, speed, pitch)
51
+
52
+	textKey := cache.GetTextKey(ossObjectKey)
53
+
54
+	textKeyExists := cache.TextKeyExists(textKey)
55
+
56
+	//fmt.Println("TextToSpeechMain %s%s%s%s", text,ossObjectKey,textKey,textKeyExists)
57
+
58
+	if textKeyExists == true {
59
+
60
+		//get url
61
+		mp3Url := service.GetUrl(ossObjectKey)
62
+
63
+		ctx.Redirect(302, mp3Url)
64
+		return
65
+	} else {
66
+		appConfig := config.C.App
67
+		if appConfig.InChina == true {
68
+
69
+			text = url.QueryEscape(text)
70
+			resp, err := http.Get(appConfig.ApiHost + "/v1/en/tts?text="+text+"&languageCode="+languageCode+"&gender="+gender)
71
+
72
+			if err != nil || (resp.StatusCode != http.StatusFound && resp.StatusCode != http.StatusOK) {
73
+
74
+				fmt.Println("service.Get Api", err , appConfig.ApiHost + "/v1/en/tts?text="+text+"&languageCode="+languageCode+"&gender="+gender)
75
+				fmt.Println("service.Get Api resp", resp, err)
76
+				request.Fail(ctx, 400, errors.New("Get Api error"))
77
+				return
78
+			}
79
+
80
+			mp3Url := service.GetUrl(ossObjectKey)
81
+
82
+			//set cache
83
+			cache.SetOssObject(textKey, ossObjectKey)
84
+
85
+			ctx.Redirect(302, mp3Url)
86
+			return
87
+		} else {
88
+
89
+			//todo 先从db查一下缓存, 线上数据尽量走其它方式缓存到DB
90
+
91
+			AudioContent, err := service.TextToSpeech(text, voiceName, languageCode, speed, pitch)
92
+			if err != nil {
93
+				fmt.Println("service.TextToSpeech Error", err)
94
+				request.Fail(ctx, 400, errors.New("Error: service.TextToSpeech"))
95
+				return
96
+			}
97
+
98
+			// todo 因hk地区和国内oss不通
99
+			service.UploadHkOss(ossObjectKey, AudioContent)
100
+			uploadResult, err := service.UploadOss(ossObjectKey, AudioContent)
101
+
102
+			if err != nil {
103
+				fmt.Println("service.UploadHkOss Error", err)
104
+				request.Fail(ctx, 400, errors.New("Error: service.UploadOss"))
105
+				return
106
+			}
107
+			if uploadResult == true {
108
+
109
+				//get url
110
+				mp3Url := service.GetUrl(ossObjectKey)
111
+				//set cache
112
+				cache.SetOssObject(textKey, ossObjectKey)
113
+				//set db
114
+				mysql.CreateCorpusTts(text, textKey, languageCode, voiceName, ossObjectKey, speed, pitch)
115
+
116
+				ctx.Redirect(302, mp3Url)
117
+				return
118
+			}
119
+
120
+			//set cache
121
+		}
122
+
123
+	}
124
+}
125
+
126
+
127
+func GetDefaultVoiceName(LanguageCode, Gender string) string {
128
+
129
+	//todo
130
+	switch LanguageCode {
131
+
132
+	case "en-GB":
133
+		if Gender == "FEMALE" {
134
+
135
+			return "en-GB-Wavenet-C"
136
+		} else {
137
+
138
+			return "en-GB-Wavenet-B"
139
+		}
140
+	case "en-US":
141
+		if Gender == "FEMALE" {
142
+
143
+			return "en-US-Wavenet-C"
144
+		} else {
145
+
146
+			return "en-US-Wavenet-B"
147
+		}
148
+	}
149
+
150
+	return ""
151
+
152
+	//en-GB	en-GB-Wavenet-B	MALE
153
+	//en-GB	en-GB-Wavenet-C	FEMALE
154
+
155
+	//en-US	en-US-Wavenet-B	MALE
156
+	//en-US	en-US-Wavenet-C	FEMALE
157
+
158
+	//通过键值获取默认配置: languageCode + gender
159
+
160
+}
161
+

+ 61
- 0
cmd/http/http.go Datei anzeigen

@@ -0,0 +1,61 @@
1
+package http
2
+
3
+import (
4
+	"github.com/sirupsen/logrus"
5
+	"os"
6
+	"os/signal"
7
+	"strings"
8
+	"syscall"
9
+
10
+	"git.links123.net/Slate/CorpusAI/cmd/http/router"
11
+	"git.links123.net/Slate/CorpusAI/service/store/cache"
12
+	"git.links123.net/Slate/CorpusAI/service/store/mysql"
13
+	"github.com/braintree/manners"
14
+	"github.com/spf13/cobra"
15
+)
16
+
17
+
18
+// RunCommand cobra subcommand http
19
+func RunCommand() *cobra.Command {
20
+	var host, port string
21
+	cmd := &cobra.Command{
22
+		Use:   "http",
23
+		Short: "Run the http service",
24
+		Run: func(cmd *cobra.Command, args []string) {
25
+			go Start(host, port)
26
+			// 阻塞退出 捕获信号
27
+			signalChan := make(chan os.Signal)
28
+			signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
29
+			logrus.Infof("caught signal %+v, begin garbage collection", <-signalChan)
30
+			Stop()
31
+		},
32
+	}
33
+	cmd.PersistentFlags().StringVarP(&host, "host", "o", "127.0.0.1", "server hostname")
34
+	cmd.PersistentFlags().StringVarP(&port, "port", "p", "8080", "server port")
35
+
36
+	return cmd
37
+}
38
+
39
+// Start use for cobra or testing
40
+func Start(host, port string) {
41
+	// init db
42
+	mysql.Init()
43
+	// init cache
44
+	cache.Init()
45
+	// build router
46
+	r := router.BuildRouter()
47
+	// start server
48
+	err := manners.ListenAndServe(strings.Join([]string{host, port}, ":"), r)
49
+	if err != nil {
50
+		panic(err)
51
+	}
52
+}
53
+
54
+// Stop stop the http service graceful
55
+func Stop() {
56
+	if manners.Close() {
57
+		logrus.Info("http server stopped")
58
+	}
59
+	cache.Close()
60
+	mysql.Close()
61
+}

+ 16
- 0
cmd/http/middleware/auth.go Datei anzeigen

@@ -0,0 +1,16 @@
1
+package middleware
2
+
3
+import (
4
+	"git.links123.net/links123.com/pkg/middleware/auth"
5
+	"git.links123.net/Slate/CorpusAI/config"
6
+	"git.links123.net/Slate/CorpusAI/service/store/cache"
7
+	"github.com/gin-gonic/gin"
8
+)
9
+
10
+func Auth() gin.HandlerFunc {
11
+	return auth.Auth(config.C.App.Secret, cache.AuthSessionStore())
12
+}
13
+
14
+func OptionalAuth() gin.HandlerFunc {
15
+	return auth.OptionalAuth(config.C.App.Secret)
16
+}

+ 29
- 0
cmd/http/router/router.go Datei anzeigen

@@ -0,0 +1,29 @@
1
+package router
2
+
3
+import (
4
+	"git.links123.net/Slate/CorpusAI/cmd/http/handler"
5
+	_ "git.links123.net/Slate/CorpusAI/docs"
6
+	"git.links123.net/links123.com/pkg/middleware/cors"
7
+	"github.com/gin-gonic/gin"
8
+	"github.com/swaggo/gin-swagger"
9
+	"github.com/swaggo/gin-swagger/swaggerFiles"
10
+)
11
+
12
+var r = gin.Default()
13
+
14
+func BuildRouter() *gin.Engine {
15
+	r.RedirectTrailingSlash = true
16
+	r.RedirectFixedPath = true
17
+
18
+	// Whether the service is running
19
+	r.HEAD("/", handler.Healthy)
20
+	if gin.Mode() == gin.DebugMode {
21
+		r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
22
+	}
23
+
24
+	r.Use(cors.Cors())
25
+
26
+	registerV1Router()
27
+
28
+	return r
29
+}

+ 18
- 0
cmd/http/router/v1.go Datei anzeigen

@@ -0,0 +1,18 @@
1
+package router
2
+
3
+import (
4
+	"git.links123.net/Slate/CorpusAI/cmd/http/handler"
5
+)
6
+
7
+func registerV1Router() {
8
+	// Auth
9
+	public := r.Group("/v1")
10
+	//auth := r.Group("/v1").Use(middleware.Auth())
11
+	//optionAuth := r.Group("/v1").Use(middleware.OptionalAuth())
12
+
13
+	public.GET("/en/audio_base_url", handler.GetAudioBaseUrl)
14
+
15
+	//Text string, LanguageCode, Gender string
16
+	public.GET("/en/tts", handler.TextToSpeechMain)
17
+	public.POST("/en/stt", handler.SpeechToText)
18
+}

+ 18
- 0
cmd/job/job.go Datei anzeigen

@@ -0,0 +1,18 @@
1
+package job
2
+
3
+import (
4
+	"fmt"
5
+	"github.com/spf13/cobra"
6
+	"strings"
7
+)
8
+
9
+func RunCommand() *cobra.Command {
10
+	cmd := &cobra.Command{
11
+		Use:   "job",
12
+		Short: "Run the job service",
13
+		Run: func(cmd *cobra.Command, args []string) {
14
+			fmt.Println("Echo: " + strings.Join(args, " "))
15
+		},
16
+	}
17
+	return cmd
18
+}

+ 23
- 0
cmd/version/version.go Datei anzeigen

@@ -0,0 +1,23 @@
1
+package version
2
+
3
+import (
4
+	"fmt"
5
+	"os"
6
+
7
+	"github.com/spf13/cobra"
8
+)
9
+
10
+// Version command return api version for tracking binary in different server
11
+func RunCommand(apiVersion, gitCommit, built string) *cobra.Command {
12
+	return &cobra.Command{
13
+		Use:     "version",
14
+		Short:   "The version of forum",
15
+		Aliases: []string{"v"},
16
+		Run: func(cmd *cobra.Command, args []string) {
17
+			fmt.Fprintln(os.Stdout, "Server:")
18
+			fmt.Fprintln(os.Stdout, " Api Version:         ", apiVersion)
19
+			fmt.Fprintln(os.Stdout, " Git commit:          ", gitCommit)
20
+			fmt.Fprintln(os.Stdout, " Built:               ", built)
21
+		},
22
+	}
23
+}

+ 129
- 0
config/config.go Datei anzeigen

@@ -0,0 +1,129 @@
1
+package config
2
+
3
+import (
4
+	"io/ioutil"
5
+	"log"
6
+	"path"
7
+	"strings"
8
+
9
+	"github.com/Unknwon/i18n"
10
+	"github.com/gin-gonic/gin"
11
+	"github.com/joho/godotenv"
12
+	"github.com/sirupsen/logrus"
13
+	"github.com/spf13/viper"
14
+)
15
+
16
+var (
17
+	C appConfig
18
+)
19
+
20
+type appConfig struct {
21
+	App struct {
22
+		Debug  bool   `mapstructure:"debug"`
23
+		InChina  bool   `mapstructure:"in_china"`
24
+		Secret string `mapstructure:"secret"`
25
+		ApiHost string `mapstructure:"api_host"`
26
+		CNHost string `mapstructure:"api_cn_host"`
27
+		HKHost string `mapstructure:"api_hk_host"`
28
+	} `mapstructure:"app"`
29
+	DB struct {
30
+		Host               string `mapstructure:"host"`
31
+		User               string `mapstructure:"user"`
32
+		Password           string `mapstructure:"password"`
33
+		Name               string `mapstructure:"name"`
34
+		MaxIdleConnections int    `mapstructure:"max_idle_connections"`
35
+		MaxOpenConnections int    `mapstructure:"max_open_connections"`
36
+	} `mapstructure:"db"`
37
+	OSS struct {
38
+		AccessKey string `mapstructure:"access_key"`
39
+		SecretKey string `mapstructure:"secret_key"`
40
+		Bucket string `mapstructure:"bucket"`
41
+		EndPoint string `mapstructure:"end_point"`
42
+		HkBucket string `mapstructure:"hk_bucket"`
43
+		HkEndPoint string `mapstructure:"hk_end_point"`
44
+	} `mapstructure:"oss"`
45
+	Redis struct {
46
+		Address  string `mapstructure:"address"`
47
+		Password string `mapstructure:"password"`
48
+		PoolSize int    `mapstructure:"pool_size"`
49
+	} `mapstructure:"redis"`
50
+
51
+	MaxMind struct {
52
+		UserId  string `mapstructure:"user_id"`
53
+		LicenseKey string `mapstructure:"license_key"`
54
+	} `mapstructure:"maxmind"`
55
+}
56
+
57
+func init() {
58
+
59
+	// 去掉烦人的gin提示,在http模块中会根据需要打开
60
+	gin.SetMode(gin.ReleaseMode)
61
+
62
+	// load .env file for testing
63
+	godotenv.Load()
64
+
65
+	//fmt.Println("Connect to redis error", err)
66
+	// app
67
+	viper.SetDefault("app.debug", true)
68
+	viper.SetDefault("app.secret", "123456")
69
+
70
+	viper.SetDefault("app.in_china", true)
71
+	viper.SetDefault("app.api_host", "")
72
+	viper.SetDefault("app.api_cn_host", "")
73
+	viper.SetDefault("app.api_hk_host", "")
74
+
75
+	// db
76
+	viper.SetDefault("db.host", "localhost:3306")
77
+	viper.SetDefault("db.user", "user")
78
+	viper.SetDefault("db.password", "password")
79
+	viper.SetDefault("db.name", "name")
80
+	viper.SetDefault("db.max_idle_connections", 20)
81
+	viper.SetDefault("db.max_open_connections", 50)
82
+
83
+	// redis
84
+	viper.SetDefault("redis.address", "127.0.0.1")
85
+	viper.SetDefault("redis.password", "")
86
+	viper.SetDefault("redis.pool_size", 10)
87
+
88
+	// oss
89
+	viper.SetDefault("oss.access_key", "")
90
+	viper.SetDefault("oss.secret_key", "")
91
+	viper.SetDefault("oss.bucket", "")
92
+	viper.SetDefault("oss.end_point", "")
93
+	viper.SetDefault("oss.hk_bucket", "")
94
+	viper.SetDefault("oss.hk_end_point", "")
95
+
96
+	// MaxMind
97
+	viper.SetDefault("maxmind.user_id", "")
98
+	viper.SetDefault("maxmind.license_key", "")
99
+
100
+	// bind env
101
+	viper.SetEnvPrefix("CorpusAI")
102
+	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
103
+	viper.AutomaticEnv()
104
+
105
+	// unmarshal config to struct
106
+	if err := viper.Unmarshal(&C); err != nil {
107
+		log.Fatalf("viper.Unmarshal error: %v\n", err)
108
+	}
109
+
110
+	// set run mode
111
+	switch C.App.Debug {
112
+	case true:
113
+		logrus.SetLevel(logrus.DebugLevel)
114
+	case false:
115
+		gin.SetMode(gin.ReleaseMode)
116
+	}
117
+
118
+	// load language file for i18n
119
+	files, err := ioutil.ReadDir("languages")
120
+	if err == nil {
121
+		for _, file := range files {
122
+			if err := i18n.SetMessage(strings.TrimSuffix(file.Name(), path.Ext(file.Name())), "languages/"+file.Name()); err != nil {
123
+				log.Fatalf("i18n.SetMessage error: %v\n", err)
124
+			}
125
+		}
126
+
127
+		i18n.SetDefaultLang("en-US")
128
+	}
129
+}

+ 199
- 0
docs/docs.go Datei anzeigen

@@ -0,0 +1,199 @@
1
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
2
+// This file was generated by swaggo/swag at
3
+// 2018-10-17 20:53:09.6090834 +0800 CST m=+0.097091800
4
+
5
+package docs
6
+
7
+import (
8
+	"github.com/swaggo/swag"
9
+)
10
+
11
+var doc = `{
12
+    "swagger": "2.0",
13
+    "info": {
14
+        "description": "Campus text to speech / speech to text.",
15
+        "title": "Campus AI API",
16
+        "termsOfService": "http://swagger.io/terms/",
17
+        "contact": {
18
+            "name": "API Support",
19
+            "email": "slate@links123.com"
20
+        },
21
+        "license": {},
22
+        "version": "1.0"
23
+    },
24
+    "host": "campusai.links123.net",
25
+    "basePath": "/v1",
26
+    "paths": {
27
+        "/en/audio_base_url": {
28
+            "get": {
29
+                "description": "获取音频接口base url(主要用于海内外接口地址分开)",
30
+                "consumes": [
31
+                    "application/json"
32
+                ],
33
+                "produces": [
34
+                    "application/json"
35
+                ],
36
+                "tags": [
37
+                    "语料库"
38
+                ],
39
+                "summary": "获取音频接口base url",
40
+                "parameters": [
41
+                    {
42
+                        "type": "string",
43
+                        "description": "IP地址, 默认无需传, 自动获取",
44
+                        "name": "ip",
45
+                        "in": "query"
46
+                    }
47
+                ],
48
+                "responses": {
49
+                    "200": {
50
+                        "description": "{\"audio_base_url\": \"https://campusai.links123.com/v1/en/tts\",\"country_code\": \"国家code\",\"ip\": \"ip地址\"}",
51
+                        "schema": {
52
+                            "type": "string"
53
+                        }
54
+                    },
55
+                    "400": {
56
+                        "description": "{\"msg\": \"error info\"}",
57
+                        "schema": {
58
+                            "type": "string"
59
+                        }
60
+                    },
61
+                    "500": {
62
+                        "description": "{\"msg\": \"error info\"}",
63
+                        "schema": {
64
+                            "type": "string"
65
+                        }
66
+                    }
67
+                }
68
+            }
69
+        },
70
+        "/en/stt": {
71
+            "post": {
72
+                "description": "speech to Text",
73
+                "consumes": [
74
+                    "application/json"
75
+                ],
76
+                "produces": [
77
+                    "application/json"
78
+                ],
79
+                "tags": [
80
+                    "语料库"
81
+                ],
82
+                "summary": "语音转文本",
83
+                "parameters": [
84
+                    {
85
+                        "type": "file",
86
+                        "description": "发音文件",
87
+                        "name": "file",
88
+                        "in": "query",
89
+                        "required": true
90
+                    },
91
+                    {
92
+                        "type": "string",
93
+                        "description": "语言,en英文,zh中文,默认英文",
94
+                        "name": "language",
95
+                        "in": "path"
96
+                    },
97
+                    {
98
+                        "type": "integer",
99
+                        "description": "码率,8/16 默认16",
100
+                        "name": "rate",
101
+                        "in": "path"
102
+                    }
103
+                ],
104
+                "responses": {
105
+                    "200": {
106
+                        "description": "test",
107
+                        "schema": {
108
+                            "type": "string"
109
+                        }
110
+                    },
111
+                    "400": {
112
+                        "description": "{\"msg\": \"error info\"}",
113
+                        "schema": {
114
+                            "type": "string"
115
+                        }
116
+                    },
117
+                    "500": {
118
+                        "description": "{\"msg\": \"error info\"}",
119
+                        "schema": {
120
+                            "type": "string"
121
+                        }
122
+                    }
123
+                }
124
+            }
125
+        },
126
+        "/en/tts": {
127
+            "get": {
128
+                "description": "Text to speech",
129
+                "consumes": [
130
+                    "application/json"
131
+                ],
132
+                "produces": [
133
+                    "application/json"
134
+                ],
135
+                "tags": [
136
+                    "语料库"
137
+                ],
138
+                "summary": "文本转语音",
139
+                "parameters": [
140
+                    {
141
+                        "type": "string",
142
+                        "description": "单词/短语/句子",
143
+                        "name": "text",
144
+                        "in": "query"
145
+                    },
146
+                    {
147
+                        "enum": [
148
+                            "en-US",
149
+                            "en-GB"
150
+                        ],
151
+                        "type": "string",
152
+                        "description": "en-US(美音)/en-GB(英音)",
153
+                        "name": "languageCode",
154
+                        "in": "query"
155
+                    },
156
+                    {
157
+                        "enum": [
158
+                            "MALE",
159
+                            "FEMALE"
160
+                        ],
161
+                        "type": "string",
162
+                        "description": "MALE(男音)/FEMALE(女音)",
163
+                        "name": "gender",
164
+                        "in": "query"
165
+                    }
166
+                ],
167
+                "responses": {
168
+                    "302": {
169
+                        "description": "https://lnks123-campus-tts-hk.oss-cn-hongkong.aliyuncs.com/xxxx.mp3",
170
+                        "schema": {
171
+                            "type": "string"
172
+                        }
173
+                    },
174
+                    "400": {
175
+                        "description": "{\"msg\": \"error info\"}",
176
+                        "schema": {
177
+                            "type": "string"
178
+                        }
179
+                    },
180
+                    "500": {
181
+                        "description": "{\"msg\": \"error info\"}",
182
+                        "schema": {
183
+                            "type": "string"
184
+                        }
185
+                    }
186
+                }
187
+            }
188
+        }
189
+    }
190
+}`
191
+
192
+type s struct{}
193
+
194
+func (s *s) ReadDoc() string {
195
+	return doc
196
+}
197
+func init() {
198
+	swag.Register(swag.Name, &s{})
199
+}

+ 180
- 0
docs/swagger/swagger.json Datei anzeigen

@@ -0,0 +1,180 @@
1
+{
2
+    "swagger": "2.0",
3
+    "info": {
4
+        "description": "Campus text to speech / speech to text.",
5
+        "title": "Campus AI API",
6
+        "termsOfService": "http://swagger.io/terms/",
7
+        "contact": {
8
+            "name": "API Support",
9
+            "email": "slate@links123.com"
10
+        },
11
+        "license": {},
12
+        "version": "1.0"
13
+    },
14
+    "host": "campusai.links123.net",
15
+    "basePath": "/v1",
16
+    "paths": {
17
+        "/en/audio_base_url": {
18
+            "get": {
19
+                "description": "获取音频接口base url(主要用于海内外接口地址分开)",
20
+                "consumes": [
21
+                    "application/json"
22
+                ],
23
+                "produces": [
24
+                    "application/json"
25
+                ],
26
+                "tags": [
27
+                    "语料库"
28
+                ],
29
+                "summary": "获取音频接口base url",
30
+                "parameters": [
31
+                    {
32
+                        "type": "string",
33
+                        "description": "IP地址, 默认无需传, 自动获取",
34
+                        "name": "ip",
35
+                        "in": "query"
36
+                    }
37
+                ],
38
+                "responses": {
39
+                    "200": {
40
+                        "description": "{\"audio_base_url\": \"https://campusai.links123.com/v1/en/tts\",\"country_code\": \"国家code\",\"ip\": \"ip地址\"}",
41
+                        "schema": {
42
+                            "type": "string"
43
+                        }
44
+                    },
45
+                    "400": {
46
+                        "description": "{\"msg\": \"error info\"}",
47
+                        "schema": {
48
+                            "type": "string"
49
+                        }
50
+                    },
51
+                    "500": {
52
+                        "description": "{\"msg\": \"error info\"}",
53
+                        "schema": {
54
+                            "type": "string"
55
+                        }
56
+                    }
57
+                }
58
+            }
59
+        },
60
+        "/en/stt": {
61
+            "post": {
62
+                "description": "speech to Text",
63
+                "consumes": [
64
+                    "application/json"
65
+                ],
66
+                "produces": [
67
+                    "application/json"
68
+                ],
69
+                "tags": [
70
+                    "语料库"
71
+                ],
72
+                "summary": "语音转文本",
73
+                "parameters": [
74
+                    {
75
+                        "type": "file",
76
+                        "description": "发音文件",
77
+                        "name": "file",
78
+                        "in": "query",
79
+                        "required": true
80
+                    },
81
+                    {
82
+                        "type": "string",
83
+                        "description": "语言,en英文,zh中文,默认英文",
84
+                        "name": "language",
85
+                        "in": "path"
86
+                    },
87
+                    {
88
+                        "type": "integer",
89
+                        "description": "码率,8/16 默认16",
90
+                        "name": "rate",
91
+                        "in": "path"
92
+                    }
93
+                ],
94
+                "responses": {
95
+                    "200": {
96
+                        "description": "test",
97
+                        "schema": {
98
+                            "type": "string"
99
+                        }
100
+                    },
101
+                    "400": {
102
+                        "description": "{\"msg\": \"error info\"}",
103
+                        "schema": {
104
+                            "type": "string"
105
+                        }
106
+                    },
107
+                    "500": {
108
+                        "description": "{\"msg\": \"error info\"}",
109
+                        "schema": {
110
+                            "type": "string"
111
+                        }
112
+                    }
113
+                }
114
+            }
115
+        },
116
+        "/en/tts": {
117
+            "get": {
118
+                "description": "Text to speech",
119
+                "consumes": [
120
+                    "application/json"
121
+                ],
122
+                "produces": [
123
+                    "application/json"
124
+                ],
125
+                "tags": [
126
+                    "语料库"
127
+                ],
128
+                "summary": "文本转语音",
129
+                "parameters": [
130
+                    {
131
+                        "type": "string",
132
+                        "description": "单词/短语/句子",
133
+                        "name": "text",
134
+                        "in": "query"
135
+                    },
136
+                    {
137
+                        "enum": [
138
+                            "en-US",
139
+                            "en-GB"
140
+                        ],
141
+                        "type": "string",
142
+                        "description": "en-US(美音)/en-GB(英音)",
143
+                        "name": "languageCode",
144
+                        "in": "query"
145
+                    },
146
+                    {
147
+                        "enum": [
148
+                            "MALE",
149
+                            "FEMALE"
150
+                        ],
151
+                        "type": "string",
152
+                        "description": "MALE(男音)/FEMALE(女音)",
153
+                        "name": "gender",
154
+                        "in": "query"
155
+                    }
156
+                ],
157
+                "responses": {
158
+                    "302": {
159
+                        "description": "https://lnks123-campus-tts-hk.oss-cn-hongkong.aliyuncs.com/xxxx.mp3",
160
+                        "schema": {
161
+                            "type": "string"
162
+                        }
163
+                    },
164
+                    "400": {
165
+                        "description": "{\"msg\": \"error info\"}",
166
+                        "schema": {
167
+                            "type": "string"
168
+                        }
169
+                    },
170
+                    "500": {
171
+                        "description": "{\"msg\": \"error info\"}",
172
+                        "schema": {
173
+                            "type": "string"
174
+                        }
175
+                    }
176
+                }
177
+            }
178
+        }
179
+    }
180
+}

+ 121
- 0
docs/swagger/swagger.yaml Datei anzeigen

@@ -0,0 +1,121 @@
1
+basePath: /v1
2
+host: campusai.links123.net
3
+info:
4
+  contact:
5
+    email: slate@links123.com
6
+    name: API Support
7
+  description: Campus text to speech / speech to text.
8
+  license: {}
9
+  termsOfService: http://swagger.io/terms/
10
+  title: Campus AI API
11
+  version: "1.0"
12
+paths:
13
+  /en/audio_base_url:
14
+    get:
15
+      consumes:
16
+      - application/json
17
+      description: 获取音频接口base url(主要用于海内外接口地址分开)
18
+      parameters:
19
+      - description: IP地址, 默认无需传, 自动获取
20
+        in: query
21
+        name: ip
22
+        type: string
23
+      produces:
24
+      - application/json
25
+      responses:
26
+        "200":
27
+          description: '{"audio_base_url": "https://campusai.links123.com/v1/en/tts","country_code":
28
+            "国家code","ip": "ip地址"}'
29
+          schema:
30
+            type: string
31
+        "400":
32
+          description: '{"msg": "error info"}'
33
+          schema:
34
+            type: string
35
+        "500":
36
+          description: '{"msg": "error info"}'
37
+          schema:
38
+            type: string
39
+      summary: 获取音频接口base url
40
+      tags:
41
+      - 语料库
42
+  /en/stt:
43
+    post:
44
+      consumes:
45
+      - application/json
46
+      description: speech to Text
47
+      parameters:
48
+      - description: 发音文件
49
+        in: query
50
+        name: file
51
+        required: true
52
+        type: file
53
+      - description: 语言,en英文,zh中文,默认英文
54
+        in: path
55
+        name: language
56
+        type: string
57
+      - description: 码率,8/16 默认16
58
+        in: path
59
+        name: rate
60
+        type: integer
61
+      produces:
62
+      - application/json
63
+      responses:
64
+        "200":
65
+          description: test
66
+          schema:
67
+            type: string
68
+        "400":
69
+          description: '{"msg": "error info"}'
70
+          schema:
71
+            type: string
72
+        "500":
73
+          description: '{"msg": "error info"}'
74
+          schema:
75
+            type: string
76
+      summary: 语音转文本
77
+      tags:
78
+      - 语料库
79
+  /en/tts:
80
+    get:
81
+      consumes:
82
+      - application/json
83
+      description: Text to speech
84
+      parameters:
85
+      - description: 单词/短语/句子
86
+        in: query
87
+        name: text
88
+        type: string
89
+      - description: en-US(美音)/en-GB(英音)
90
+        enum:
91
+        - en-US
92
+        - en-GB
93
+        in: query
94
+        name: languageCode
95
+        type: string
96
+      - description: MALE(男音)/FEMALE(女音)
97
+        enum:
98
+        - MALE
99
+        - FEMALE
100
+        in: query
101
+        name: gender
102
+        type: string
103
+      produces:
104
+      - application/json
105
+      responses:
106
+        "302":
107
+          description: https://lnks123-campus-tts-hk.oss-cn-hongkong.aliyuncs.com/xxxx.mp3
108
+          schema:
109
+            type: string
110
+        "400":
111
+          description: '{"msg": "error info"}'
112
+          schema:
113
+            type: string
114
+        "500":
115
+          description: '{"msg": "error info"}'
116
+          schema:
117
+            type: string
118
+      summary: 文本转语音
119
+      tags:
120
+      - 语料库
121
+swagger: "2.0"

+ 1
- 0
go.mod Datei anzeigen

@@ -0,0 +1 @@
1
+module git.links123.net/Slate/CorpusAI

+ 0
- 0
internal/.gitkeep Datei anzeigen


+ 1
- 0
languages/en-US.ini Datei anzeigen

@@ -0,0 +1 @@
1
+hi = hello, %s

+ 1
- 0
languages/zh-CN.ini Datei anzeigen

@@ -0,0 +1 @@
1
+hi = 你好,%s

+ 36
- 0
main.go Datei anzeigen

@@ -0,0 +1,36 @@
1
+package main
2
+
3
+import (
4
+
5
+	"git.links123.net/Slate/CorpusAI/cmd/http"
6
+	"git.links123.net/Slate/CorpusAI/cmd/version"
7
+	"git.links123.net/Slate/CorpusAI/cmd/job"
8
+	"github.com/spf13/cobra"
9
+)
10
+
11
+var apiVersion, gitCommit, built string
12
+
13
+// @title Campus AI API
14
+// @version 1.0
15
+// @description Campus text to speech / speech to text.
16
+// @termsOfService http://swagger.io/terms/
17
+
18
+// @contact.name API Support
19
+// @contact.email slate@links123.com
20
+
21
+// @host campusai.links123.net
22
+// @BasePath /v1
23
+func main() {
24
+	rootCmd := &cobra.Command{
25
+		Use:   "CorpusAI",
26
+		Short: "CorpusAI api for the project of links123.com's campus",
27
+	}
28
+
29
+	rootCmd.AddCommand(http.RunCommand())
30
+	rootCmd.AddCommand(version.RunCommand(apiVersion, gitCommit, built))
31
+	rootCmd.AddCommand(job.RunCommand())
32
+
33
+	if err := rootCmd.Execute(); err != nil {
34
+		panic(err)
35
+	}
36
+}

+ 0
- 0
migrations/.gitkeep Datei anzeigen


+ 16
- 0
migrations/table.sql Datei anzeigen

@@ -0,0 +1,16 @@
1
+
2
+
3
+CREATE TABLE `corpus_tts` (
4
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
5
+  `text` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '翻译的文本',
6
+  `uniq_key` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '唯一键值',
7
+  `language_code` enum('en-US','en-GB','zh-CN') COLLATE utf8mb4_unicode_ci NOT NULL,
8
+  `voice_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发音名称',
9
+  `speed` float(8,0) NOT NULL DEFAULT '1',
10
+  `pitch` float(8,0) NOT NULL DEFAULT '0',
11
+  `url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
12
+  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
14
+  PRIMARY KEY (`id`),
15
+  KEY `idx_uniq_key` (`uniq_key`)
16
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

+ 1
- 0
service/api/README.md Datei anzeigen

@@ -0,0 +1 @@
1
+接收外部请求数据的结构体和对请求进行相应的结构体

+ 36
- 0
service/api/common.go Datei anzeigen

@@ -0,0 +1,36 @@
1
+package api
2
+
3
+import (
4
+	"fmt"
5
+)
6
+
7
+const (
8
+	EnUS = 1
9
+	ZhCN = 2
10
+)
11
+
12
+type CommonParams struct {
13
+	TargetStr string `form:"lang"`
14
+	Target    int
15
+	Page      int `form:"page"`
16
+	Limit     int `form:"limit"`
17
+}
18
+
19
+func (cp *CommonParams) Check() error {
20
+	switch cp.TargetStr {
21
+	case "", "en-US":
22
+		cp.Target = EnUS
23
+	case "zh-CN":
24
+		cp.Target = ZhCN
25
+	default:
26
+		return fmt.Errorf("Unexpected target param: %s", cp.TargetStr)
27
+	}
28
+
29
+	if cp.Limit == 0 {
30
+		cp.Limit = 10
31
+	}
32
+	if cp.Page == 0 {
33
+		cp.Page = 1
34
+	}
35
+	return nil
36
+}

+ 17
- 0
service/geoip_tool.go Datei anzeigen

@@ -0,0 +1,17 @@
1
+package service
2
+
3
+import (
4
+	"github.com/savaki/geoip2"
5
+	"git.links123.net/Slate/CorpusAI/config"
6
+)
7
+
8
+// @Description Ip to country code
9
+func Ip2Country(ip string) string {
10
+
11
+	MaxMindConfig := config.C.MaxMind
12
+
13
+	api := geoip2.New(MaxMindConfig.UserId, MaxMindConfig.LicenseKey)
14
+	resp, _ := api.Country(nil, ip)
15
+
16
+	return resp.Country.IsoCode
17
+}

+ 12
- 0
service/service.go Datei anzeigen

@@ -0,0 +1,12 @@
1
+package service
2
+
3
+import (
4
+	"errors"
5
+)
6
+
7
+var (
8
+	ErrInvalidID  = errors.New("Invalid id in request url")
9
+	ErrForbidden  = errors.New("It's not your resource")
10
+	ErrInterface  = errors.New("Unmarshal data to nil interface")
11
+	ErrUnexpected = errors.New("unexpected server error")
12
+)

+ 119
- 0
service/speech_to_text.go Datei anzeigen

@@ -0,0 +1,119 @@
1
+package service
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/md5"
6
+	"encoding/base64"
7
+	"encoding/hex"
8
+	"encoding/json"
9
+	"errors"
10
+	"fmt"
11
+	"io/ioutil"
12
+	"mime/multipart"
13
+	"net/http"
14
+	"net/url"
15
+	"strings"
16
+	"time"
17
+)
18
+
19
+// Xfyun 讯飞云调用对象
20
+type Xfyun struct {
21
+	URL string
22
+	ID  string
23
+	KEY string
24
+}
25
+
26
+// XfyunResult 讯飞云返回结果
27
+type XfyunResult struct {
28
+	Code string `json:"code"`
29
+	Data string `json:"data"`
30
+	Desc string `json:"desc"`
31
+	Sid  string `json:"sid"`
32
+}
33
+
34
+// XfyunParam 讯飞云请求参数
35
+type XfyunParam struct {
36
+	Aue        string `json:"aue"`
37
+	EngineType string `json:"engine_type"`
38
+}
39
+
40
+// SuccessCode 讯飞云成功代码
41
+const SuccessCode = "success"
42
+
43
+// SpeechToText speech to text input File
44
+func SpeechToText(file multipart.File, lang string, rateStr string) (string, error) {
45
+	xfyun := Xfyun{
46
+		URL: "http://api.xfyun.cn/v1/service/v1/iat",
47
+		ID:  "5bbaeda1",
48
+		KEY: "25c68c82b45309d35a7f14069107746b",
49
+	}
50
+	wav, err := ioutil.ReadAll(file)
51
+
52
+	if err != nil {
53
+		return "", err
54
+	}
55
+	//
56
+	base64Audio := base64.StdEncoding.EncodeToString(wav)
57
+	u := url.Values{}
58
+	u.Set("audio", base64Audio)
59
+	body := u.Encode()
60
+	xfyunParam := XfyunParam{
61
+		Aue:        "raw",
62
+		EngineType: "sms",
63
+	}
64
+
65
+	if lang != "zh" {
66
+		xfyunParam.EngineType += "-en"
67
+	}
68
+	if rateStr == "8" {
69
+		xfyunParam.EngineType += "8k"
70
+	} else {
71
+		xfyunParam.EngineType += "16k"
72
+	}
73
+	xfyunParamString, _ := json.Marshal(xfyunParam)
74
+
75
+	fmt.Println(string(xfyunParamString))
76
+
77
+	xParam := base64.StdEncoding.EncodeToString(xfyunParamString)
78
+	xTime := time.Now().Unix()
79
+
80
+	h := md5.New()
81
+	h.Write([]byte(fmt.Sprintf("%s%d%s", xfyun.KEY, xTime, xParam)))
82
+	xChecksum := hex.EncodeToString(h.Sum(nil))
83
+
84
+	client := &http.Client{}
85
+
86
+	req, _ := http.NewRequest("POST", xfyun.URL, bytes.NewBuffer([]byte(body)))
87
+
88
+	req.Header.Set("Accept-Encoding", "identity")
89
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
90
+	req.Header.Add("X-Appid", xfyun.ID)
91
+	req.Header.Add("X-CurTime", fmt.Sprintf("%d", xTime))
92
+	req.Header.Add("X-Param", xParam)
93
+	req.Header.Add("X-CheckSum", xChecksum)
94
+
95
+	resp, err := client.Do(req)
96
+	if err != nil {
97
+		return "", nil
98
+	}
99
+
100
+	html, err := ioutil.ReadAll(resp.Body)
101
+	if err != nil {
102
+		return "", err
103
+	}
104
+
105
+	xfyunResult := XfyunResult{}
106
+	err = json.Unmarshal(html, &xfyunResult)
107
+	if err != nil {
108
+		return "", nil
109
+	}
110
+	var text string
111
+	if xfyunResult.Desc == SuccessCode {
112
+		text = xfyunResult.Data
113
+	} else {
114
+		err = errors.New(xfyunResult.Desc)
115
+		return "", err
116
+	}
117
+
118
+	return strings.TrimSpace(text), nil
119
+}

+ 9
- 0
service/store/cache/auth.go Datei anzeigen

@@ -0,0 +1,9 @@
1
+package cache
2
+
3
+import (
4
+	"git.links123.net/links123.com/pkg/middleware/auth"
5
+)
6
+
7
+func AuthSessionStore() auth.Session {
8
+	return session
9
+}

+ 14
- 0
service/store/cache/auth_test.go Datei anzeigen

@@ -0,0 +1,14 @@
1
+package cache_test
2
+
3
+import (
4
+	"testing"
5
+
6
+	"git.links123.net/Slate/CorpusAI/service/store/cache"
7
+)
8
+
9
+func TestAuthSessionStore(t *testing.T) {
10
+	store := cache.AuthSessionStore()
11
+	if store == nil {
12
+		t.Error("init auth session store error")
13
+	}
14
+}

+ 60
- 0
service/store/cache/cache.go Datei anzeigen

@@ -0,0 +1,60 @@
1
+package cache
2
+
3
+import (
4
+	"sync"
5
+	"time"
6
+
7
+	"git.links123.net/Slate/CorpusAI/config"
8
+	"git.links123.net/links123.com/pkg/middleware/auth"
9
+	"github.com/go-redis/redis"
10
+	"github.com/sirupsen/logrus"
11
+)
12
+
13
+var (
14
+	client  *redis.Client
15
+	session auth.Session
16
+	once    = &sync.Once{}
17
+)
18
+
19
+// Init 初始化缓存
20
+func Init() {
21
+	once.Do(func() {
22
+		redisConfig := config.C.Redis
23
+
24
+		client = redis.NewClient(&redis.Options{
25
+			Addr:     redisConfig.Address,
26
+			Password: redisConfig.Password,
27
+			PoolSize: redisConfig.PoolSize,
28
+		})
29
+
30
+		for {
31
+			if err := client.Ping().Err(); err != nil {
32
+				logrus.Warn("waiting for redis server start...")
33
+				time.Sleep(3 * time.Second)
34
+				continue
35
+			}
36
+
37
+			logrus.Info("connect to redis successful")
38
+
39
+			break
40
+		}
41
+
42
+		if config.C.App.Debug {
43
+			session = auth.NewDebugSessionStore()
44
+		} else {
45
+			session = auth.NewRedisSessionStore(redisConfig.Address, redisConfig.Password)
46
+		}
47
+	})
48
+}
49
+
50
+// Close 退出时关闭链接
51
+func Close() {
52
+	if client != nil {
53
+		err := client.Close()
54
+		if err != nil {
55
+			logrus.Errorf("close redis connection error: %s", err.Error())
56
+			return
57
+		}
58
+		logrus.Info("redis connection closed")
59
+	}
60
+}

+ 10
- 0
service/store/cache/cache_test.go Datei anzeigen

@@ -0,0 +1,10 @@
1
+package cache_test
2
+
3
+import (
4
+	"os"
5
+	"testing"
6
+)
7
+
8
+func TestMain(m *testing.M) {
9
+	os.Exit(m.Run())
10
+}

+ 51
- 0
service/store/cache/tts.go Datei anzeigen

@@ -0,0 +1,51 @@
1
+package cache
2
+
3
+import (
4
+	"crypto/md5"
5
+	"fmt"
6
+	"github.com/go-redis/redis"
7
+)
8
+
9
+func GetTextKey(ossObjectKey string) string {
10
+	data := []byte(ossObjectKey)
11
+	hash := md5.Sum(data)
12
+	md5OssObjectKey := fmt.Sprintf("%x", hash)
13
+	return "tts:" + md5OssObjectKey;
14
+}
15
+
16
+func GetOssObject(key string) (string, error) {
17
+	value, err := client.Get(key).Result()
18
+	if err == redis.Nil {
19
+
20
+		return "", err
21
+		//fmt.Println("key2 does not exist")
22
+	} else if err != nil {
23
+
24
+		return "", err
25
+	}
26
+
27
+	return value, nil
28
+}
29
+
30
+func SetOssObject(key string, value string) error {
31
+	err := client.Set(key, value, 0).Err()
32
+	if err != nil {
33
+		return err
34
+	}
35
+
36
+	return nil
37
+}
38
+
39
+func TextKeyExists(textKey string) bool {
40
+	value, err := client.Exists(textKey).Result()
41
+	if err == redis.Nil {
42
+
43
+		return false
44
+		//fmt.Println("key2 does not exist")
45
+	} else if value == 1 {
46
+
47
+		return true
48
+	}
49
+
50
+	return false
51
+}

+ 70
- 0
service/store/mysql/mysql.go Datei anzeigen

@@ -0,0 +1,70 @@
1
+package mysql
2
+
3
+import (
4
+	"time"
5
+
6
+	"git.links123.net/Slate/CorpusAI/config"
7
+	"git.links123.net/Slate/CorpusAI/service/api"
8
+	"github.com/sirupsen/logrus"
9
+	"upper.io/db.v3/lib/sqlbuilder"
10
+	"upper.io/db.v3/mysql"
11
+)
12
+
13
+
14
+var (
15
+	db_session sqlbuilder.Database
16
+)
17
+
18
+// Init 初始化数据库
19
+func Init() {
20
+	dbConfig := config.C.DB
21
+	settings := mysql.ConnectionURL{
22
+		Host:     dbConfig.Host,
23
+		Database: dbConfig.Name,
24
+		User:     dbConfig.User,
25
+		Password: dbConfig.Password,
26
+	}
27
+
28
+	var err error
29
+	if db_session, err = mysql.Open(settings); err != nil {
30
+		logrus.WithField("dsn", settings.String()).Info("connect db failed")
31
+		panic(err)
32
+	}
33
+
34
+	logrus.WithField("dsn", settings.String()).Info("connect db success")
35
+
36
+	db_session.SetMaxIdleConns(dbConfig.MaxIdleConnections)
37
+	db_session.SetMaxOpenConns(dbConfig.MaxOpenConnections)
38
+
39
+	for {
40
+		if err := db_session.Ping(); err != nil {
41
+			logrus.Warn("waiting for mysql server start...")
42
+			time.Sleep(3 * time.Second)
43
+			continue
44
+		}
45
+
46
+		logrus.Info("connect to mysql successful")
47
+
48
+		break
49
+	}
50
+
51
+	if config.C.App.Debug {
52
+		db_session.SetLogging(true)
53
+	}
54
+}
55
+
56
+// Close 在程序退出时关闭连接
57
+func Close() {
58
+	if db_session != nil {
59
+		err := db_session.Close()
60
+		if err != nil {
61
+			logrus.Errorf("error close mysql connection: %s", err.Error())
62
+			return
63
+		}
64
+	}
65
+	logrus.Info("mysql connection close")
66
+}
67
+
68
+func paging(selector sqlbuilder.Selector, cp api.CommonParams) sqlbuilder.Paginator {
69
+	return selector.Paginate(uint(cp.Limit)).Page(uint(cp.Page))
70
+}

+ 38
- 0
service/store/mysql/tts.go Datei anzeigen

@@ -0,0 +1,38 @@
1
+package mysql
2
+
3
+const ttsCollection = `lnk_corpus_tts`
4
+type Tts struct {
5
+	ID                     int64  `db:"id"`
6
+	Text                   string  `db:"text"`			//翻译的文本
7
+	UniqKey                string  `db:"uniq_key"`		//唯一键值
8
+	LanguageCode           string  `db:"language_code"` //'en-US','en-GB','zh-CN'
9
+	VoiceName              string  `db:"voice_name"`	//发音名称
10
+	Speed                  float64  `db:"speed"`
11
+	Pitch                  float64  `db:"pitch"`
12
+	Url                    string  `db:"url"`
13
+}
14
+
15
+func CreateCorpusTts(text, uniqKey, languageCode, voiceName, url string, speed, pitch float64) error {
16
+
17
+	var tts = &Tts{
18
+		Text: text,
19
+		UniqKey: uniqKey,
20
+		LanguageCode: languageCode,
21
+		VoiceName: voiceName,
22
+		Url: url,
23
+		Speed: speed,
24
+		Pitch: pitch,
25
+	}
26
+
27
+	_, err := db_session.Collection(ttsCollection).Insert(tts)
28
+	if err != nil {
29
+		return err
30
+	}
31
+
32
+	return nil
33
+}
34
+
35
+func GetByKey(uniqKey string) error {
36
+
37
+	return nil
38
+}

+ 111
- 0
service/text_to_speech.go Datei anzeigen

@@ -0,0 +1,111 @@
1
+package service
2
+
3
+import (
4
+	"bytes"
5
+	"cloud.google.com/go/texttospeech/apiv1"
6
+	"crypto/md5"
7
+	"fmt"
8
+	"git.links123.net/Slate/CorpusAI/config"
9
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
10
+	texttospeechpb "google.golang.org/genproto/googleapis/cloud/texttospeech/v1"
11
+	"golang.org/x/net/context"
12
+)
13
+
14
+// @Description Get TTS Oss unique key
15
+func GetTtsOssKey(Text string, VoiceName string, LanguageCode string, Speed float64, Pitch float64) string {
16
+
17
+	data := []byte(Text)
18
+	hash := md5.Sum(data)
19
+	md5Value := fmt.Sprintf("%x", hash)
20
+
21
+	key := fmt.Sprintf("%s/%s/%s/%s/%f_%f.mp3", "tts/en/google", md5Value, LanguageCode, VoiceName, Speed, Pitch)
22
+
23
+	return key
24
+}
25
+
26
+func GetUrl(ossObjectKey string) string {
27
+
28
+	ossConfig := config.C.OSS
29
+
30
+	appConfig := config.C.App
31
+	if appConfig.InChina == true {
32
+
33
+		return fmt.Sprintf("https://%s.%s/%s", ossConfig.Bucket, ossConfig.EndPoint, ossObjectKey)
34
+
35
+	} else {
36
+		return fmt.Sprintf("https://%s.%s/%s", ossConfig.HkBucket, ossConfig.HkEndPoint, ossObjectKey)
37
+	}
38
+}
39
+
40
+// @Description Upload AudioContent to Oss
41
+func UploadOss(ossObjectKey string, AudioContent []byte) (bool , error) {
42
+	ossConfig := config.C.OSS
43
+	client, err := oss.New(ossConfig.EndPoint, ossConfig.AccessKey, ossConfig.SecretKey)
44
+	bucket, err := client.Bucket(ossConfig.Bucket)
45
+	if err != nil {
46
+		return false, fmt.Errorf("texttospeech.UploadOss Bucket error: %v", err)
47
+	}
48
+
49
+	// 上传Byte数组。
50
+	err = bucket.PutObject(ossObjectKey, bytes.NewReader(AudioContent))
51
+	isExist, err := bucket.IsObjectExist(ossObjectKey)
52
+	if err != nil {
53
+		return false, fmt.Errorf("texttospeech.UploadOss Put error: %v", err)
54
+	}
55
+	return isExist, nil
56
+}
57
+
58
+func UploadHkOss(ossObjectKey string, AudioContent []byte) (bool , error) {
59
+	ossConfig := config.C.OSS
60
+	client, err := oss.New(ossConfig.HkEndPoint, ossConfig.AccessKey, ossConfig.SecretKey)
61
+	bucket, err := client.Bucket(ossConfig.HkBucket)
62
+	if err != nil {
63
+		return false, fmt.Errorf("texttospeech.UploadOss Bucket error: %v", err)
64
+	}
65
+
66
+	// 上传Byte数组。
67
+	err = bucket.PutObject(ossObjectKey, bytes.NewReader(AudioContent))
68
+	isExist, err := bucket.IsObjectExist(ossObjectKey)
69
+	if err != nil {
70
+		return false, fmt.Errorf("texttospeech.UploadOss Put error: %v", err)
71
+	}
72
+	return isExist, nil
73
+}
74
+
75
+// @Description Text to speech
76
+func TextToSpeech(Text string, VoiceName string, LanguageCode string, Speed float64, Pitch float64) ([]byte , error) {
77
+
78
+	ctx := context.Background()
79
+
80
+	client, err := texttospeech.NewClient(ctx)
81
+	if err != nil {
82
+		return nil, fmt.Errorf("texttospeech.NewClient error: %v", err)
83
+	}
84
+
85
+	req := texttospeechpb.SynthesizeSpeechRequest{
86
+		// Set the text input to be synthesized.
87
+		Input: &texttospeechpb.SynthesisInput{
88
+			InputSource: &texttospeechpb.SynthesisInput_Text{Text: Text},
89
+		},
90
+		// Build the voice request, select the language code ("en-US") and the SSML
91
+		// voice gender ("neutral").
92
+		Voice: &texttospeechpb.VoiceSelectionParams{
93
+			Name: VoiceName,
94
+			LanguageCode: LanguageCode,
95
+			//SsmlGender:   texttospeechpb.SsmlVoiceGender_NEUTRAL,
96
+		},
97
+		// Select the type of audio file you want returned.
98
+		AudioConfig: &texttospeechpb.AudioConfig{
99
+			AudioEncoding: texttospeechpb.AudioEncoding_MP3,
100
+			SpeakingRate: Speed,
101
+			Pitch: Pitch,
102
+		},
103
+	}
104
+
105
+	resp, err := client.SynthesizeSpeech(ctx, &req)
106
+	if err != nil {
107
+		return nil, fmt.Errorf("texttospeech.SynthesizeSpeech error: %v", err)
108
+	}
109
+
110
+	return resp.AudioContent, nil
111
+}