clipper/backend/server/server.go

151 lines
3.9 KiB
Go
Raw Normal View History

2021-09-25 17:00:19 +00:00
package server
import (
2021-10-22 19:30:09 +00:00
"context"
2021-11-01 05:28:40 +00:00
"fmt"
2021-10-29 12:52:31 +00:00
"io"
2021-10-22 19:30:09 +00:00
"log"
"net/http"
"os"
2021-09-25 17:00:19 +00:00
"time"
2021-10-22 19:30:09 +00:00
pbMediaSet "git.netflux.io/rob/clipper/generated/pb/media_set"
"git.netflux.io/rob/clipper/media"
2021-10-29 12:52:31 +00:00
"git.netflux.io/rob/clipper/youtube"
2021-11-01 05:28:40 +00:00
"github.com/google/uuid"
2021-10-22 19:30:09 +00:00
"github.com/improbable-eng/grpc-web/go/grpcweb"
"google.golang.org/grpc"
2021-11-01 05:28:40 +00:00
"google.golang.org/grpc/codes"
2021-10-22 19:30:09 +00:00
"google.golang.org/grpc/grpclog"
"google.golang.org/protobuf/types/known/durationpb"
2021-09-25 17:00:19 +00:00
)
2021-11-01 05:28:40 +00:00
type ResponseError struct {
error
ResponseCode codes.Code
}
func (r *ResponseError) Error() string {
return fmt.Sprintf("An unexpected error occurred: %v (error code = %d).", r.error.Error(), r.ResponseCode)
}
func (r *ResponseError) Unwrap() error {
return r.error
}
func newResponseError(err error, code codes.Code) *ResponseError {
return &ResponseError{error: err, ResponseCode: code}
}
2021-09-25 17:00:19 +00:00
type Options struct {
2021-10-29 12:52:31 +00:00
BindAddr string
Timeout time.Duration
2021-11-01 05:28:40 +00:00
Store media.Store
2021-10-29 12:52:31 +00:00
YoutubeClient youtube.YoutubeClient
S3Client media.S3Client
2021-09-25 17:00:19 +00:00
}
2021-10-29 12:52:31 +00:00
const (
2021-11-01 05:28:40 +00:00
getAudioTimeout = time.Minute * 5
2021-10-29 12:52:31 +00:00
)
2021-11-01 05:28:40 +00:00
// mediaSetServiceController implements gRPC controller for MediaSetService
type mediaSetServiceController struct {
pbMediaSet.UnimplementedMediaSetServiceServer
2021-10-22 19:30:09 +00:00
2021-11-01 05:28:40 +00:00
mediaSetService *media.MediaSetService
2021-10-22 19:30:09 +00:00
}
2021-11-01 05:28:40 +00:00
// Get returns a pbMediaSet.MediaSet
func (c *mediaSetServiceController) Get(ctx context.Context, request *pbMediaSet.GetRequest) (*pbMediaSet.MediaSet, error) {
mediaSet, err := c.mediaSetService.Get(ctx, request.GetYoutubeId())
2021-10-22 19:30:09 +00:00
if err != nil {
2021-11-01 05:28:40 +00:00
return nil, newResponseError(err, codes.Unknown)
2021-10-22 19:30:09 +00:00
}
result := pbMediaSet.MediaSet{
2021-11-01 05:28:40 +00:00
Id: mediaSet.YoutubeID,
2021-10-22 19:30:09 +00:00
Audio: &pbMediaSet.MediaSet_Audio{
2021-11-01 19:33:45 +00:00
Channels: int32(mediaSet.Audio.Channels),
Frames: mediaSet.Audio.Frames,
ApproxFrames: mediaSet.Audio.ApproxFrames,
SampleRate: int32(mediaSet.Audio.SampleRate),
YoutubeItag: int32(mediaSet.Audio.YoutubeItag),
MimeType: mediaSet.Audio.MimeType,
2021-10-22 19:30:09 +00:00
},
Video: &pbMediaSet.MediaSet_Video{
2021-11-01 19:33:45 +00:00
Duration: durationpb.New(mediaSet.Video.Duration),
YoutubeItag: int32(mediaSet.Video.YoutubeItag),
MimeType: mediaSet.Video.MimeType,
2021-10-22 19:30:09 +00:00
},
}
return &result, nil
}
2021-11-01 05:28:40 +00:00
// GetAudio streams the progress report of GetAudio.
func (c *mediaSetServiceController) GetAudio(request *pbMediaSet.GetAudioRequest, stream pbMediaSet.MediaSetService_GetAudioServer) error {
ctx, cancel := context.WithTimeout(context.Background(), getAudioTimeout)
2021-10-29 12:52:31 +00:00
defer cancel()
2021-11-01 05:28:40 +00:00
id, err := uuid.Parse(request.GetId())
if err != nil {
return newResponseError(err, codes.Unknown)
}
reader, err := c.mediaSetService.GetAudio(ctx, id, int(request.GetNumBins()))
2021-10-29 12:52:31 +00:00
if err != nil {
2021-11-01 05:28:40 +00:00
return newResponseError(err, codes.Unknown)
2021-10-29 12:52:31 +00:00
}
for {
progress, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
2021-11-01 05:28:40 +00:00
return newResponseError(err, codes.Unknown)
2021-10-29 12:52:31 +00:00
}
// TODO: consider using int32 throughout the backend flow to avoid this.
peaks := make([]int32, len(progress.Peaks))
for i, p := range progress.Peaks {
peaks[i] = int32(p)
}
2021-11-01 05:28:40 +00:00
progressPb := pbMediaSet.GetAudioProgress{
2021-10-29 12:52:31 +00:00
PercentCompleted: progress.PercentComplete,
Peaks: peaks,
}
stream.Send(&progressPb)
}
2021-10-22 19:30:09 +00:00
return nil
}
func Start(options Options) error {
grpcServer := grpc.NewServer()
2021-11-01 05:28:40 +00:00
fetchMediaSetService := media.NewMediaSetService(options.Store, options.YoutubeClient, options.S3Client)
2021-10-29 12:52:31 +00:00
2021-11-01 05:28:40 +00:00
pbMediaSet.RegisterMediaSetServiceServer(grpcServer, &mediaSetServiceController{mediaSetService: fetchMediaSetService})
2021-10-22 19:30:09 +00:00
grpclog.SetLogger(log.New(os.Stdout, "server: ", log.LstdFlags))
2021-10-29 12:52:31 +00:00
// TODO: proper CORS support
2021-10-22 19:30:09 +00:00
grpcWebServer := grpcweb.WrapServer(grpcServer, grpcweb.WithOriginFunc(func(string) bool { return true }))
handler := func(w http.ResponseWriter, r *http.Request) {
grpcWebServer.ServeHTTP(w, r)
}
httpServer := http.Server{
Addr: options.BindAddr,
ReadTimeout: options.Timeout,
WriteTimeout: options.Timeout,
Handler: http.HandlerFunc(handler),
}
return httpServer.ListenAndServe()
2021-09-25 17:00:19 +00:00
}