Skip to content

Commit

Permalink
Working state.
Browse files Browse the repository at this point in the history
Will probably squash or split this into proper commits later.
  • Loading branch information
icedream committed Mar 17, 2024
1 parent cca6ffa commit 47cca64
Show file tree
Hide file tree
Showing 28 changed files with 650 additions and 369 deletions.
42 changes: 34 additions & 8 deletions cmd/storage-discover/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/json"
"errors"
"flag"
"io"
"log"
Expand Down Expand Up @@ -107,6 +109,11 @@ func main() {
runEngineLibraryUI(grpcURL)
}

func marshalJSON(v any) []byte {
s, _ := json.Marshal(v)
return s
}

func runEngineLibraryUI(grpcURL string) {
ctx := context.Background()
connection, err := eaas.DialContext(ctx, grpcURL)
Expand Down Expand Up @@ -139,23 +146,42 @@ func runEngineLibraryUI(grpcURL string) {
if err != nil {
panic(err)
}
var pageSize uint32 = 100
var pageSize uint32 = 25
getTracksResp, err := connection.GetTracks(ctx, &enginelibrary.GetTracksRequest{
PageSize: &pageSize,
})
if err != nil {
panic(err)
}
for _, track := range getTracksResp.GetTracks() {
log.Printf("Track: %s", string(marshalJSON(track)))
getTrackResp, err := connection.GetTrack(ctx, &enginelibrary.GetTrackRequest{
TrackId: track.GetMetadata().Id,
})
if err != nil {
log.Println("\tfailed to GetTrack on this track")
continue
}
log.Printf("\t%+v", getTrackResp)
}
for _, playlist := range getLibraryResp.GetPlaylists() {
log.Printf("Playlist %q (%q)", playlist.GetTitle(), playlist.GetListType())

log.Printf("Playlist: %s", string(marshalJSON(playlist)))
getTracksResp, err := connection.GetTracks(ctx, &enginelibrary.GetTracksRequest{
PlaylistId: playlist.Id,
PageSize: &pageSize,
})
if errors.Is(err, io.EOF) {
// BUG - empty playlist causes EOF, reconnect
connection, err = eaas.DialContext(ctx, grpcURL)
if err != nil {
panic(err)
}
}
if err != nil {
panic(err)
}
for _, track := range getTracksResp.GetTracks() {
metadata := track.GetMetadata()
if metadata == nil {
continue
}
log.Printf("\tTrack %s", metadata.String())
log.Printf("\tTrack: ID %s", track.GetMetadata().GetId())
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/storage/.gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
*.m4a filter=lfs diff=lfs merge=lfs -text
*.beatgrid filter=lfs diff=lfs merge=lfs -text
*.waveform filter=lfs diff=lfs merge=lfs -text
2 changes: 1 addition & 1 deletion cmd/storage/Icedream - Whiplash (Radio Edit).m4a
Git LFS file not shown
3 changes: 3 additions & 0 deletions cmd/storage/Icedream - Whiplash (Radio Edit).m4a.beatgrid
Git LFS file not shown
3 changes: 3 additions & 0 deletions cmd/storage/Icedream - Whiplash (Radio Edit).m4a.waveform
Git LFS file not shown
61 changes: 52 additions & 9 deletions cmd/storage/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,81 @@ import (
"bytes"
_ "embed"
"fmt"
"path/filepath"
"strconv"
"strings"

"github.com/dhowden/tag"
"github.com/google/uuid"
"github.com/icedream/go-stagelinq/eaas"
"github.com/icedream/go-stagelinq/eaas/proto/enginelibrary"
"google.golang.org/protobuf/types/known/timestamppb"
)

// Imports needed for image resizing (see commented out code for it)
// import (
// "image"
// _ "image/jpeg"
// _ "image/png"
// )

var (
demoTrackFileName = "Icedream - Whiplash (Radio Edit).flac"
demoTrackFileName = "Icedream - Whiplash (Radio Edit).m4a"
demoLibrary = "12eceaa2-f81a-4b63-b196-94648a3bdd95"
demoLibraryName = "Demo Library"
demoPlaylist = "55ab0c7c-6c35-429a-81d0-25b039a34a9f"
demoPlaylistName = "Demo Playlist"
demoPlaylistTrackCount uint32 = 1
demoTrackIDs []string
demoTrackURL = "/demo/" + demoTrackFileName
demoTrackLength = uint32(len(demoTrackBytes))
demoTrackMetadata enginelibrary.TrackMetadata
demoTrackArtwork []byte
demoTrackIDs = []string{
"1 " + demoLibrary,
}
// HACK - imitating original Engine DJ software behavior by using Windows paths
demoTrackURL = filepath.Join("C:", "demo", demoTrackFileName)
// HACK - imitating original Engine DJ software behavior by adding brackets.
demoTrackURLGRPC = fmt.Sprintf("<%s>", filepath.ToSlash(demoTrackURL))
demoTrackLength = uint32(len(demoTrackBytes))
demoTrackMetadata enginelibrary.TrackMetadata
demoTrackArtwork []byte
demoToken eaas.Token = eaas.Token{
0x5e, 0xff, 0xae, 0x59, 0x12, 0x88, 0x29, 0x30,
0xde, 0xad, 0xc0, 0xde, 0xc0, 0xff, 0xee, 0x00,
}
)

//go:embed "Icedream - Whiplash (Radio Edit).m4a"
var demoTrackBytes []byte

//go:embed "Icedream - Whiplash (Radio Edit).m4a.beatgrid"
var demoBeatGrid []byte

//go:embed "Icedream - Whiplash (Radio Edit).m4a.waveform"
var demoOverviewWaveform []byte

var demoTrackPreviewArtwork []byte

func init() {
for i := 0; i < int(demoPlaylistTrackCount); i++ {
demoTrackIDs = append(demoTrackIDs, uuid.New().String())
if len(demoTrackIDs) == 0 {
for i := 0; i < int(demoPlaylistTrackCount); i++ {
demoTrackIDs = append(demoTrackIDs, uuid.New().String())
}
}

demoTrackMetadata.DateAdded = timestamppb.Now()
if metadata, err := tag.ReadFrom(bytes.NewReader(demoTrackBytes)); err == nil {
if metadata.Picture() != nil {
demoTrackArtwork = metadata.Picture().Data
demoTrackPreviewArtwork = demoTrackArtwork
// // If you wanna be nice to the hardware, you can have the server
// // shrink down the artwork. I don't think even the original Engine
// // DJ software does that though.
// img, _, err := image.Decode(bytes.NewReader(demoTrackArtwork))
// if err == nil {
// img = resize.Resize(240, 240, img, resize.Lanczos2)
// }
// var b bytes.Buffer
// if err := jpeg.Encode(&b, img, &jpeg.Options{Quality: 70}); err == nil {
// demoTrackPreviewArtwork = b.Bytes()
// }
}
if v := metadata.Artist(); len(v) > 0 {
demoTrackMetadata.Artist = &v
Expand All @@ -64,19 +105,21 @@ func init() {
}
if v, ok := metadata.Raw()["KEY"]; ok {
s := fmt.Sprint(v)
s = strings.Trim(s, "\x00 ")
demoTrackMetadata.Key = &s
}
if v, ok := metadata.Raw()["LABEL"]; ok {
s := fmt.Sprint(v)
s = strings.Trim(s, "\x00 ")
demoTrackMetadata.Label = &s
}
if v, ok := metadata.Raw()["REMIXER"]; ok {
s := fmt.Sprint(v)
s = strings.Trim(s, "\x00 ")
demoTrackMetadata.Remixer = &s
}
if v := uint32(metadata.Year()); v > 0 {
demoTrackMetadata.Year = &v
}

}
}
73 changes: 56 additions & 17 deletions cmd/storage/engine_library_service_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
_ "embed"
"fmt"
"log"
"math"
"strconv"

Expand All @@ -15,38 +16,48 @@ import (

var _ enginelibrary.EngineLibraryServiceServer = &EngineLibraryServiceServer{}

// EngineLibraryServiceServer is an example library service server
// implementation.
//
// It will provide a the demo audio file as if contained in a library with
// playlists. Some functions not needed for the task are left unimplemented.
type EngineLibraryServiceServer struct {
enginelibrary.UnimplementedEngineLibraryServiceServer
}

// EventStream implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) EventStream(ctx context.Context, req *enginelibrary.EventStreamRequest) (*enginelibrary.EventStreamResponse, error) {
log.Printf("EventStream: %+v", req)
return &enginelibrary.EventStreamResponse{
Event: []*enginelibrary.Event{},
}, nil
}

// GetCredentials implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetCredentials(context.Context, *enginelibrary.GetCredentialsRequest) (*enginelibrary.GetCredentialsResponse, error) {
func (e *EngineLibraryServiceServer) GetCredentials(ctx context.Context, req *enginelibrary.GetCredentialsRequest) (*enginelibrary.GetCredentialsResponse, error) {
log.Printf("GetCredentials: %+v", req)
panic("unimplemented")
}

// GetHistoryPlayedTracks implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetHistoryPlayedTracks(context.Context, *enginelibrary.GetHistoryPlayedTracksRequest) (*enginelibrary.GetHistoryPlayedTracksResponse, error) {
func (e *EngineLibraryServiceServer) GetHistoryPlayedTracks(ctx context.Context, req *enginelibrary.GetHistoryPlayedTracksRequest) (*enginelibrary.GetHistoryPlayedTracksResponse, error) {
log.Printf("GetHistoryPlayedTracks: %+v", req)
return &enginelibrary.GetHistoryPlayedTracksResponse{
Tracks: []*enginelibrary.HistoryPlayedTrack{},
}, nil
}

// GetHistorySessions implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetHistorySessions(context.Context, *enginelibrary.GetHistorySessionsRequest) (*enginelibrary.GetHistorySessionsResponse, error) {
func (e *EngineLibraryServiceServer) GetHistorySessions(ctx context.Context, req *enginelibrary.GetHistorySessionsRequest) (*enginelibrary.GetHistorySessionsResponse, error) {
log.Printf("GetHistorySessions: %+v", req)
return &enginelibrary.GetHistorySessionsResponse{
Sessions: []*enginelibrary.HistorySession{},
}, nil
}

// GetLibraries implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetLibraries(ctx context.Context, req *enginelibrary.GetLibrariesRequest) (*enginelibrary.GetLibrariesResponse, error) {
log.Printf("GetLibraries: %+v", req)
return &enginelibrary.GetLibrariesResponse{
Libraries: []*enginelibrary.Library{
{
Expand All @@ -59,8 +70,9 @@ func (e *EngineLibraryServiceServer) GetLibraries(ctx context.Context, req *engi

// GetLibrary implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetLibrary(ctx context.Context, req *enginelibrary.GetLibraryRequest) (*enginelibrary.GetLibraryResponse, error) {
log.Printf("GetLibrary: %+v", req)
switch req.GetLibraryId() {
case demoLibrary:
case "", demoLibrary:
return &enginelibrary.GetLibraryResponse{
Playlists: []*enginelibrary.PlaylistMetadata{
{
Expand All @@ -79,6 +91,7 @@ func (e *EngineLibraryServiceServer) GetLibrary(ctx context.Context, req *engine

// GetSearchFilters implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetSearchFilters(ctx context.Context, req *enginelibrary.GetSearchFiltersRequest) (*enginelibrary.GetSearchFiltersResponse, error) {
log.Printf("GetSearchFilters: %+v", req)
resp := &enginelibrary.GetSearchFiltersResponse{
SearchFilters: &enginelibrary.SearchFilterOptions{},
}
Expand Down Expand Up @@ -143,26 +156,40 @@ func generateDemoTrackMetadata(trackID string) *enginelibrary.TrackMetadata {
return &metadata
}

var unsetFloat64 float64 = -1

// GetTrack implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetTrack(ctx context.Context, req *enginelibrary.GetTrackRequest) (*enginelibrary.GetTrackResponse, error) {
if req.GetLibraryId() != demoLibrary && req.LibraryId != nil {
log.Printf("GetTrack: %+v", req)
if len(req.GetLibraryId()) != 0 && req.GetLibraryId() != demoLibrary {
return nil, status.Error(codes.NotFound, "library not found")
}

for _, trackID := range demoTrackIDs {
metadata := generateDemoTrackMetadata(trackID)
if trackID == req.GetTrackId() {
return &enginelibrary.GetTrackResponse{
resp := &enginelibrary.GetTrackResponse{
Blob: &enginelibrary.TrackBlob{
Type: &enginelibrary.TrackBlob_Url{
Url: &enginelibrary.TrackBlobUrl{
Url: &demoTrackURL,
Url: &demoTrackURLGRPC,
FileSize: &demoTrackLength,
},
},
},
Metadata: generateDemoTrackMetadata(trackID),
PerformanceData: nil, // TODO
}, nil
Metadata: generateDemoTrackMetadata(trackID),
PerformanceData: &enginelibrary.TrackPerformanceData{
Bpm: metadata.Bpm,
BeatGrid: demoBeatGrid,
MainCue: &enginelibrary.MainCue{
Position: &unsetFloat64,
InitialPosition: &unsetFloat64,
},
OverviewWaveform: demoOverviewWaveform,
},
}
log.Printf("=> Found demo track ID: %+v", resp)
return resp, nil
}
}

Expand All @@ -171,21 +198,31 @@ func (e *EngineLibraryServiceServer) GetTrack(ctx context.Context, req *engineli

// GetTracks implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) GetTracks(ctx context.Context, req *enginelibrary.GetTracksRequest) (*enginelibrary.GetTracksResponse, error) {
switch req.GetLibraryId() {
case "", demoLibrary:
log.Printf("GetTracks: %+v", req)
switch {
case req.GetPlaylistId() == demoPlaylist: // specific playlist
resp := &enginelibrary.GetTracksResponse{
Tracks: []*enginelibrary.ListTrack{},
}
for _, trackID := range demoTrackIDs {
resp.Tracks = append(resp.Tracks, &enginelibrary.ListTrack{
Metadata: generateDemoTrackMetadata(trackID),
PreviewArtwork: demoTrackArtwork,
PreviewArtwork: demoTrackPreviewArtwork,
})
}
return &enginelibrary.GetTracksResponse{
return resp, nil
case req.GetLibraryId() == "" || req.GetLibraryId() == demoLibrary: // specific or default library
resp := &enginelibrary.GetTracksResponse{
Tracks: []*enginelibrary.ListTrack{},
}, nil
default:
}
for _, trackID := range demoTrackIDs {
resp.Tracks = append(resp.Tracks, &enginelibrary.ListTrack{
Metadata: generateDemoTrackMetadata(trackID),
PreviewArtwork: demoTrackPreviewArtwork,
})
}
return resp, nil
default: // neither playlist nor library match
return &enginelibrary.GetTracksResponse{
Tracks: []*enginelibrary.ListTrack{},
}, nil
Expand All @@ -194,11 +231,13 @@ func (e *EngineLibraryServiceServer) GetTracks(ctx context.Context, req *enginel

// PutEvents implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) PutEvents(ctx context.Context, req *enginelibrary.PutEventsRequest) (*enginelibrary.PutEventsResponse, error) {
log.Printf("PutEvents: %+v", req)
return &enginelibrary.PutEventsResponse{}, nil
}

// SearchTracks implements enginelibrary.EngineLibraryServiceServer.
func (e *EngineLibraryServiceServer) SearchTracks(ctx context.Context, req *enginelibrary.SearchTracksRequest) (*enginelibrary.SearchTracksResponse, error) {
log.Printf("SearchTracks: %+v", req)
resp := &enginelibrary.SearchTracksResponse{
Tracks: []*enginelibrary.ListTrack{},
}
Expand Down Expand Up @@ -261,7 +300,7 @@ trackLoop:
}
resp.Tracks = append(resp.Tracks, &enginelibrary.ListTrack{
Metadata: metadata,
PreviewArtwork: demoTrackArtwork,
PreviewArtwork: demoTrackPreviewArtwork,
})
}
return resp, nil
Expand Down
Loading

0 comments on commit 47cca64

Please sign in to comment.