Push demo code to share with interviewers

main
Gabriel Belinsky 2 years ago
parent fdaef787ff
commit a2753fa627
  1. 22
      cmd/demo.go
  2. 9
      go.mod
  3. 106
      internal/apiServer/apiServer.go
  4. 42
      internal/audio/audioServer.go
  5. 35
      internal/audio/audioStorage.go
  6. 18
      internal/metadata/metadataServer.go
  7. 47
      internal/metadata/metadataStorage.go

@ -0,0 +1,22 @@
package main
import (
"fmt"
"dread.land/deepgram-demo/internal/apiServer"
"dread.land/deepgram-demo/internal/audio"
"dread.land/deepgram-demo/internal/metadata"
)
func main() {
fmt.Println("Hello, World!")
// would be better off as config
audioStorage := audio.NewAudioMemoryStorage()
metadataStorage := metadata.NewMetadataMemoryStorage()
audioServer := &audio.AudioServer{Storage: audioStorage, Metadata: metadataStorage}
metadataServer := &metadata.MetadataServer{Storage: metadataStorage}
apiServer := &apiServer.ApiServer{Audio: audioServer, Metadata: metadataServer}
apiServer.Serve()
}

@ -0,0 +1,9 @@
module dread.land/deepgram-demo
go 1.19
require (
github.com/go-audio/audio v1.0.0 // indirect
github.com/go-audio/riff v1.0.0 // indirect
github.com/go-audio/wav v1.1.0
)

@ -0,0 +1,106 @@
package apiServer
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"path"
"strconv"
"dread.land/deepgram-demo/internal/audio"
"dread.land/deepgram-demo/internal/metadata"
)
type ApiServer struct {
Audio *audio.AudioServer
Metadata *metadata.MetadataServer
}
func (apiServer *ApiServer) filesHandler(w http.ResponseWriter, r *http.Request) {
filename := path.Base(r.URL.Path)
switch r.Method {
case "GET":
// get file
contents, err := apiServer.Audio.Download(filename)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusNotFound)
return
}
w.Write(contents)
return
case "POST": // save file
contents, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusBadRequest)
}
err = apiServer.Audio.Upload(filename, contents)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusConflict)
}
return
default:
// error unsupported
http.Error(w, "unsupported", http.StatusMethodNotAllowed)
return
}
}
func (apiServer *ApiServer) metadataHandler(w http.ResponseWriter, r *http.Request) {
filename := path.Base(r.URL.Path)
switch r.Method {
case "GET":
// get metadata
meta, err := apiServer.Metadata.Info(filename)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusNotFound)
return
}
response, err := json.Marshal(meta)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
fmt.Fprint(w, string(response))
return
default:
http.Error(w, "unsupported", http.StatusMethodNotAllowed)
return
}
}
func (apiServer *ApiServer) listHandler(w http.ResponseWriter, r *http.Request) {
maxdurationString := r.URL.Query().Get("maxduration")
maxduration := 0
var err error
if maxdurationString != "" {
maxduration, err = strconv.Atoi(maxdurationString)
}
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusBadRequest)
return // error Bad Request
}
list := apiServer.Metadata.List(maxduration)
response, err := json.Marshal(list)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
fmt.Fprint(w, string(response))
}
func (apiServer *ApiServer) Serve() {
// POST and GET media
http.HandleFunc("/files/", apiServer.filesHandler)
// list media
http.HandleFunc("/files", apiServer.listHandler)
// list metadata (with filter)
http.HandleFunc("/metadata", apiServer.listHandler)
// GET metadata
http.HandleFunc("/metadata/", apiServer.metadataHandler)
log.Fatal(http.ListenAndServe(":3030", nil))
}

@ -0,0 +1,42 @@
package audio
import (
"bytes"
"errors"
"fmt"
"github.com/go-audio/wav"
"dread.land/deepgram-demo/internal/metadata"
)
type AudioServer struct {
Storage *AudioMemoryStorage
Metadata *metadata.MetadataMemoryStorage
}
func (a *AudioServer) Upload(filename string, contents []byte) error {
fmt.Println(a)
// extract duration and save as metadata
r := bytes.NewReader(contents)
decoder := wav.NewDecoder(r)
if !decoder.IsValidFile() {
return errors.New("invalid wav file")
}
dur, err := decoder.Duration()
if err != nil {
return err
}
err = a.Metadata.Put(metadata.AudioMetadata{FileName: filename, Duration: int(dur.Milliseconds())})
if err != nil {
return err
}
err = a.Storage.put(AudioFile{FileName: filename, Contents: contents})
return err
}
func (a *AudioServer) Download(filename string) ([]byte, error) {
file, err := a.Storage.get(filename)
return file.Contents, err
}

@ -0,0 +1,35 @@
package audio
import (
"errors"
)
type AudioFile struct {
FileName string
Contents []byte
}
type AudioMemoryStorage struct {
Data map[string]AudioFile
}
func NewAudioMemoryStorage() *AudioMemoryStorage {
return &AudioMemoryStorage{Data: map[string]AudioFile{}}
}
func (audio *AudioMemoryStorage) put(a AudioFile) (err error) {
_, ok := audio.Data[a.FileName]
if !ok {
audio.Data[a.FileName] = a
return nil
}
return errors.New("already exists")
}
func (audio *AudioMemoryStorage) get(filename string) (a AudioFile, err error) {
val, ok := audio.Data[filename]
if ok {
return val, nil
}
return AudioFile{}, errors.New("not found")
}

@ -0,0 +1,18 @@
package metadata
type MetadataServer struct {
Storage *MetadataMemoryStorage
}
func (m *MetadataServer) Info(filename string) (AudioMetadata, error) {
a, err := m.Storage.get(filename)
if err == nil {
return a, nil
}
return AudioMetadata{}, err
}
func (m *MetadataServer) List(maxduration int) []string {
a := m.Storage.filterMaxDuration(maxduration)
return a
}

@ -0,0 +1,47 @@
package metadata
import (
"errors"
"fmt"
)
type AudioMetadata struct {
FileName string `json:"filename"`
Duration int `json:"duration"`
}
type MetadataMemoryStorage struct {
Metadata map[string]AudioMetadata
}
func NewMetadataMemoryStorage() *MetadataMemoryStorage {
return &MetadataMemoryStorage{Metadata: map[string]AudioMetadata{}}
}
func (metadata *MetadataMemoryStorage) Put(a AudioMetadata) (err error) {
fmt.Println(metadata, a)
_, ok := metadata.Metadata[a.FileName]
if !ok {
metadata.Metadata[a.FileName] = a
return nil
}
return errors.New("already exists") // 409 Conflict
}
func (metadata *MetadataMemoryStorage) get(filename string) (a AudioMetadata, err error) {
val, ok := metadata.Metadata[filename]
if ok {
return val, nil
}
return AudioMetadata{}, errors.New("not found") // 404 Not Found
}
func (metadata *MetadataMemoryStorage) filterMaxDuration(maxDuration int) (a []string) {
result := []string{}
for _, item := range metadata.Metadata {
if (item.Duration <= maxDuration) || (maxDuration <= 0) {
result = append(result, item.FileName)
}
}
return result
}
Loading…
Cancel
Save