diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b37b3f3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM golang:1.17.2-alpine AS builder + +# Set the Current Working Directory inside the container +WORKDIR /app + +# Copy go mod and sum files +COPY go.mod go.sum ./ + +RUN go mod download + +# Copy the source from the current directory to the Working Directory inside the container +COPY . . + +RUN go build -o ttsaas + +FROM alpine:3.12 + +RUN apk update \ + && apk add bash \ + && apk add ffmpeg \ + && apk add mplayer \ + && rm -rf /var/chache/apk/* \ + && mkdir -p /app/audio \ + && addgroup -S app && adduser -S app -G app \ + && chown -R app:app /app + +USER app + +WORKDIR /app + +COPY --from=builder /app/ttsaas . + +EXPOSE 1337 + +ENTRYPOINT ["./ttsaas"] diff --git a/README.md b/README.md index 18086e0..5da9647 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,14 @@ The api can be used as a source for audio tags. For example: ``` + +## Docker + +### Build +To build the docker image, run `docker build -t vektor/ttsaas:1.1 .` + +### Run +To run the docker image, run `docker run -p 1337:1337 vektor/ttsaas:1.1` + +(This will map to port 1337, change portnumber (`1337:`) to map to another port) + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2a6d14d --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module ttsaas.com + +go 1.17 + +require github.com/hegedustibor/htgo-tts v0.0.0-20211106065519-4b33b08f698f diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..03fd796 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/hegedustibor/htgo-tts v0.0.0-20211106065519-4b33b08f698f h1:9hj9NB/nSMz1AF/uw1J51gNmbfInP8oQ446C/50o1gE= +github.com/hegedustibor/htgo-tts v0.0.0-20211106065519-4b33b08f698f/go.mod h1:Uqnv3qFrs2WtaeO2/+PQ35x4HdD4u2ZAX/PcQEEP5VY= diff --git a/main.go b/main.go index ad4d6d2..647fb64 100644 --- a/main.go +++ b/main.go @@ -1,78 +1,82 @@ package main import ( - "fmt" - "github.com/hegedustibor/htgo-tts" - "github.com/kennygrant/sanitize" - "log" - "net/http" - "os" - "os/exec" - "strconv" - "strings" - "time" + "fmt" + "io" + "log" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "time" + + htgotts "github.com/hegedustibor/htgo-tts" ) const audioFolder = "audio" +const fileName = "speech" const defaultPort = 1337 const volume = 2 func main() { - http.HandleFunc("/", serveSpeech) - port := defaultPort - if os.Getenv("TTSAAS_PORT") != "" { - var err error - port, err = strconv.Atoi(os.Getenv("TTSAAS_PORT")) - if err != nil { - log.Fatalf("Port environment variable set, but could not convert it to int %s\n", err) - } - } - log.Printf("Starting text to speech as a service on port %d\n", port) - if err := http.ListenAndServe(":"+strconv.Itoa(port), nil); err != nil { - panic(err) - } + http.HandleFunc("/", serveSpeech) + port := defaultPort + if os.Getenv("TTSAAS_PORT") != "" { + var err error + port, err = strconv.Atoi(os.Getenv("TTSAAS_PORT")) + if err != nil { + log.Fatalf("Port environment variable set, but could not convert it to int %s\n", err) + } + } + log.Printf("Starting text to speech as a service on port %d\n", port) + if err := http.ListenAndServe(":"+strconv.Itoa(port), nil); err != nil { + panic(err) + } } func serveSpeech(w http.ResponseWriter, r *http.Request) { - addCORSHeader(w) + addCORSHeader(w) + b, _ := io.ReadAll(r.Body) + urlParts := strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") + if len(urlParts) < 1 { + http.Error(w, "bad request", http.StatusBadRequest) + log.Println("Bad request") + return + } + sentence := string(b) - urlParts := strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") - if len(urlParts) < 1 { - http.Error(w, "bad request", http.StatusBadRequest) - log.Println("Bad request") - return - } - sentence := sanitize.BaseName(urlParts[0]) + if len(sentence) == 0 { + fmt.Fprintln(w, "TTSAAS is running...") + return + } - // Save audio file to audio folder - speech := htgotts.Speech{Folder: audioFolder, Language: "no"} - err := speech.Speak(sentence) - if err != nil { - http.Error(w, "server error", http.StatusInternalServerError) - log.Printf("Error converting text to speech: %s\n", err) - return + speech := htgotts.Speech{Folder: audioFolder, Language: "no"} + speechFile, err := speech.CreateSpeechFile(sentence,fileName) + if err != nil { + log.Printf("CreateSpeechFile fail %v", err) } - fileURI := audioFolder + "/" + sentence + ".mp3" - loudFileURI := audioFolder + "/" + sentence + "LOUD" + ".mp3" - cmdString := fmt.Sprintf("ffmpeg -y -i %s -filter:a \"volume=%d\" %s", fileURI, volume, loudFileURI) - - cmd := exec.Command("bash", "-c", cmdString) - buf, err := cmd.Output() - if err != nil { - http.Error(w, "server error", http.StatusInternalServerError) - log.Printf("Error increasing audio volume: %s: %s\n", err, string(buf)) - log.Printf(cmdString) - return - } + fileURI := speechFile + loudFileURI := audioFolder + "/" + fileName + "LOUD" + ".mp3" + cmdString := fmt.Sprintf("ffmpeg -y -i %s -filter:a \"volume=%d\" %s", fileURI, volume, loudFileURI) + cmd := exec.Command("bash", "-c", cmdString) + buf, err := cmd.CombinedOutput() + e := os.Remove(speechFile) + if (err != nil || e != nil) { + http.Error(w, "server error", http.StatusInternalServerError) + log.Printf("Error increasing audio volume: %s: %s\n", err, string(buf)) + log.Printf(cmdString) + return + } - time.Sleep(500 * time.Millisecond) - http.ServeFile(w, r, loudFileURI) + time.Sleep(500 * time.Millisecond) + http.ServeFile(w, r, loudFileURI) } func addCORSHeader(w http.ResponseWriter) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", - "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", + "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") }