generated from VLADIMIR/template
up
This commit is contained in:
@@ -1544,6 +1544,7 @@ message UploadPlaylistTrackReq {
|
|||||||
string name = 2;
|
string name = 2;
|
||||||
string filename = 3;
|
string filename = 3;
|
||||||
bytes data = 4;
|
bytes data = 4;
|
||||||
|
int32 groupID = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UploadPlaylistTrackRsp {
|
message UploadPlaylistTrackRsp {
|
||||||
|
|||||||
@@ -4866,6 +4866,10 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "byte"
|
"format": "byte"
|
||||||
|
},
|
||||||
|
"groupID": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
proto "evening_detective_server/proto"
|
proto "evening_detective_server/proto"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPlaylistOrderErrorCodes(t *testing.T) {
|
func TestPlaylistOrderErrorCodes(t *testing.T) {
|
||||||
@@ -48,3 +49,28 @@ func TestPlaylistOrderRequiresAuthentication(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUploadPlaylistTrackGroupCompatibility(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
body string
|
||||||
|
want int32
|
||||||
|
}{
|
||||||
|
{"old request", `{"playlistID":1,"name":"Track","filename":"track.mp3","data":"AQ=="}`, 0},
|
||||||
|
{"group request", `{"playlistID":1,"groupID":7,"name":"Track","filename":"track.mp3","data":"AQ=="}`, 7},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var req proto.UploadPlaylistTrackReq
|
||||||
|
if err := protojson.Unmarshal([]byte(tc.body), &req); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if req.GetGroupID() != tc.want {
|
||||||
|
t.Fatalf("groupID: got %d, want %d", req.GetGroupID(), tc.want)
|
||||||
|
}
|
||||||
|
_, err := (&server{}).UploadPlaylistTrack(context.Background(), &req)
|
||||||
|
if status.Code(err) != codes.PermissionDenied {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ func (s *server) UploadPlaylistTrack(ctx context.Context, req *proto.UploadPlayl
|
|||||||
if err := s.playlistService.UploadTrack(
|
if err := s.playlistService.UploadTrack(
|
||||||
ctx,
|
ctx,
|
||||||
int(req.GetPlaylistID()),
|
int(req.GetPlaylistID()),
|
||||||
|
int(req.GetGroupID()),
|
||||||
req.GetName(),
|
req.GetName(),
|
||||||
req.GetFilename(),
|
req.GetFilename(),
|
||||||
req.GetData(),
|
req.GetData(),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func TestExportSnapshotAndEditableGame(t *testing.T) {
|
|||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
selected, err := music.Create(ctx, "Selected by screen")
|
selected, err := music.Create(ctx, "Selected by screen")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
requireNoError(t, music.AddTrack(ctx, selected.ID, "Grouped", "grouped.mp3"))
|
requireNoError(t, music.AddTrack(ctx, selected.ID, 0, "Grouped", "grouped.mp3"))
|
||||||
selected, err = music.SaveGroup(ctx, selected.ID, 0, "Final", "stop")
|
selected, err = music.SaveGroup(ctx, selected.ID, 0, "Final", "stop")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
selected, err = music.SetTrackGroup(ctx, selected.ID, selected.Tracks[0].ID, selected.Groups[0].ID)
|
selected, err = music.SetTrackGroup(ctx, selected.ID, selected.Tracks[0].ID, selected.Groups[0].ID)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestGroupEditingProtectsLivePlayback(t *testing.T) {
|
|||||||
p, err := f.repo.Create(ctx, "Music")
|
p, err := f.repo.Create(ctx, "Music")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
for _, name := range []string{"one", "two", "three"} {
|
for _, name := range []string{"one", "two", "three"} {
|
||||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||||
}
|
}
|
||||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "First", "repeat")
|
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "First", "repeat")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
@@ -101,7 +101,7 @@ func TestGroupAssignmentsAndPlaybackRejectForeignIDs(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
p, err := f.repo.Create(ctx, "One")
|
p, err := f.repo.Create(ctx, "One")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, "Track", "track.mp3"))
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, "Track", "track.mp3"))
|
||||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "Empty", "repeat")
|
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "Empty", "repeat")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
other, err := f.repo.Create(ctx, "Other")
|
other, err := f.repo.Create(ctx, "Other")
|
||||||
@@ -132,7 +132,7 @@ func TestConcurrentCompletionAdvancesOnce(t *testing.T) {
|
|||||||
p, err := f.repo.Create(ctx, "Concurrent completion")
|
p, err := f.repo.Create(ctx, "Concurrent completion")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
for _, name := range []string{"one", "two"} {
|
for _, name := range []string{"one", "two"} {
|
||||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||||
}
|
}
|
||||||
before, err := f.repo.SetMusic(ctx, f.gameID, p.ID, 0, 0, 50, false, playback, 0)
|
before, err := f.repo.SetMusic(ctx, f.gameID, p.ID, 0, 0, 50, false, playback, 0)
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func TestReorderPreservesPlaybackAndRejectsChangedMembership(t *testing.T) {
|
|||||||
p, err := f.repo.Create(ctx, "Music")
|
p, err := f.repo.Create(ctx, "Music")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
for _, name := range []string{"One", "Two", "Three"} {
|
for _, name := range []string{"One", "Two", "Three"} {
|
||||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||||
}
|
}
|
||||||
p, err = f.repo.Get(ctx, p.ID)
|
p, err = f.repo.Get(ctx, p.ID)
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
@@ -90,7 +90,7 @@ func TestConcurrentAdditionsKeepDistinctPositions(t *testing.T) {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
failures := make(chan error, count)
|
failures := make(chan error, count)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
wg.Go(func() { failures <- f.repo.AddTrack(ctx, p.ID, "Track", fmt.Sprintf("track-%d.mp3", i)) })
|
wg.Go(func() { failures <- f.repo.AddTrack(ctx, p.ID, 0, "Track", fmt.Sprintf("track-%d.mp3", i)) })
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(failures)
|
close(failures)
|
||||||
@@ -172,7 +172,7 @@ func TestReorderReadersSeeConsistentTrackAndIndex(t *testing.T) {
|
|||||||
p, err := f.repo.Create(ctx, "Snapshot")
|
p, err := f.repo.Create(ctx, "Snapshot")
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
for _, name := range []string{"a", "b", "c"} {
|
for _, name := range []string{"a", "b", "c"} {
|
||||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||||
}
|
}
|
||||||
p, err = f.repo.Get(ctx, p.ID)
|
p, err = f.repo.Get(ctx, p.ID)
|
||||||
requireNoError(t, err)
|
requireNoError(t, err)
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ func (r *Repo) Delete(ctx context.Context, id int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddTrack adds a track to a playlist.
|
// AddTrack adds a track to a playlist.
|
||||||
func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename string) error {
|
func (r *Repo) AddTrack(ctx context.Context, playlistID, groupID int, name, filename string) error {
|
||||||
tx, err := r.pool.Begin(ctx)
|
tx, err := r.pool.Begin(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -251,6 +251,14 @@ func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename stri
|
|||||||
if err := lockPlaylist(ctx, tx, playlistID); err != nil {
|
if err := lockPlaylist(ctx, tx, playlistID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if groupID != 0 {
|
||||||
|
if _, err := readGroup(ctx, tx, playlistID, groupID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := groupInUse(ctx, tx, playlistID, groupID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
var position int
|
var position int
|
||||||
if err := tx.QueryRow(
|
if err := tx.QueryRow(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -266,13 +274,14 @@ func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename stri
|
|||||||
_, err = tx.Exec(
|
_, err = tx.Exec(
|
||||||
ctx,
|
ctx,
|
||||||
`
|
`
|
||||||
INSERT INTO playlist_tracks (playlist_id, name, filename, position)
|
INSERT INTO playlist_tracks (playlist_id, name, filename, position, group_id)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4, NULLIF($5, 0))
|
||||||
`,
|
`,
|
||||||
playlistID,
|
playlistID,
|
||||||
name,
|
name,
|
||||||
filename,
|
filename,
|
||||||
position,
|
position,
|
||||||
|
groupID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package playlists_repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddTrackSavesTargetGroupAtomically(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
target string
|
||||||
|
playback int32
|
||||||
|
activeGroup bool
|
||||||
|
want error
|
||||||
|
}{
|
||||||
|
{name: "ungrouped", target: "none"},
|
||||||
|
{name: "empty group", target: "second"},
|
||||||
|
{name: "populated group", target: "first"},
|
||||||
|
{name: "missing group", target: "missing", want: ErrGroupNotFound},
|
||||||
|
{name: "foreign group", target: "foreign", want: ErrGroupNotFound},
|
||||||
|
{name: "deleted group", target: "deleted", want: ErrGroupNotFound},
|
||||||
|
{name: "playing group", target: "first", playback: 2, activeGroup: true, want: ErrPlaylistInUse},
|
||||||
|
{name: "paused group", target: "first", playback: 3, activeGroup: true, want: ErrPlaylistInUse},
|
||||||
|
{name: "another group playing", target: "second", playback: 2, activeGroup: true},
|
||||||
|
{name: "whole playlist playing", target: "second", playback: 2, want: ErrPlaylistInUse},
|
||||||
|
{name: "legacy ungrouped upload during playback", target: "none", playback: 2},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
f := newPlaylistFixture(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
p, err := f.repo.Create(ctx, "Upload")
|
||||||
|
requireNoError(t, err)
|
||||||
|
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "First", "repeat")
|
||||||
|
requireNoError(t, err)
|
||||||
|
first := p.Groups[0].ID
|
||||||
|
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "Second", "stop")
|
||||||
|
requireNoError(t, err)
|
||||||
|
second := p.Groups[1].ID
|
||||||
|
requireNoError(t, f.repo.AddTrack(ctx, p.ID, first, "Existing", "existing.mp3"))
|
||||||
|
groupID := first
|
||||||
|
switch tc.target {
|
||||||
|
case "none":
|
||||||
|
groupID = 0
|
||||||
|
case "second", "deleted":
|
||||||
|
groupID = second
|
||||||
|
case "missing":
|
||||||
|
groupID = second + 1000000
|
||||||
|
case "foreign":
|
||||||
|
other, err := f.repo.Create(ctx, "Other")
|
||||||
|
requireNoError(t, err)
|
||||||
|
other, err = f.repo.SaveGroup(ctx, other.ID, 0, "Foreign", "repeat")
|
||||||
|
requireNoError(t, err)
|
||||||
|
groupID = other.Groups[0].ID
|
||||||
|
}
|
||||||
|
if tc.target == "deleted" {
|
||||||
|
_, err := f.repo.DeleteGroup(ctx, p.ID, groupID)
|
||||||
|
requireNoError(t, err)
|
||||||
|
}
|
||||||
|
if tc.playback != 0 {
|
||||||
|
activeGroup := 0
|
||||||
|
if tc.activeGroup {
|
||||||
|
activeGroup = first
|
||||||
|
}
|
||||||
|
_, err := f.repo.SetMusic(ctx, f.gameID, p.ID, 0, 0, 50, false, tc.playback, activeGroup)
|
||||||
|
requireNoError(t, err)
|
||||||
|
}
|
||||||
|
before, err := f.repo.Get(ctx, p.ID)
|
||||||
|
requireNoError(t, err)
|
||||||
|
err = f.repo.AddTrack(ctx, p.ID, groupID, "New", "new.mp3")
|
||||||
|
if !errors.Is(err, tc.want) {
|
||||||
|
t.Fatalf("expected %v, got %v", tc.want, err)
|
||||||
|
}
|
||||||
|
after, err := f.repo.Get(ctx, p.ID)
|
||||||
|
requireNoError(t, err)
|
||||||
|
if tc.want != nil {
|
||||||
|
if !reflect.DeepEqual(before.Tracks, after.Tracks) {
|
||||||
|
t.Fatal("rejected upload changed playlist tracks")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(after.Tracks) != len(before.Tracks)+1 {
|
||||||
|
t.Fatal("upload did not add exactly one track")
|
||||||
|
}
|
||||||
|
track := after.Tracks[len(after.Tracks)-1]
|
||||||
|
if track.GroupID != groupID || track.Name != "New" || track.Position != before.Tracks[len(before.Tracks)-1].Position+1 {
|
||||||
|
t.Fatalf("track was not appended to the target group: %+v", track)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -226,7 +226,7 @@ func TestImportPreservesGameAndIsIdempotent(t *testing.T) {
|
|||||||
for _, name := range []string{"Local first", "Local second"} {
|
for _, name := range []string{"Local first", "Local second"} {
|
||||||
playlist, err := playlistRepo.Create(ctx, name)
|
playlist, err := playlistRepo.Create(ctx, name)
|
||||||
check(t, err)
|
check(t, err)
|
||||||
check(t, playlistRepo.AddTrack(ctx, playlist.ID, name, name+".mp3"))
|
check(t, playlistRepo.AddTrack(ctx, playlist.ID, 0, name, name+".mp3"))
|
||||||
}
|
}
|
||||||
source := snapshot()
|
source := snapshot()
|
||||||
source.Data.Game.Format = tc.format
|
source.Data.Game.Format = tc.format
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ func TestGroupValidationRejectsInvalidRequestsBeforeRepository(t *testing.T) {
|
|||||||
}, ErrInvalidTrack},
|
}, ErrInvalidTrack},
|
||||||
{"invalid group", func(s *Service) error { _, err := s.DeleteGroup(context.Background(), 1, 0); return err }, ErrInvalidTrack},
|
{"invalid group", func(s *Service) error { _, err := s.DeleteGroup(context.Background(), 1, 0); return err }, ErrInvalidTrack},
|
||||||
{"duplicate order", func(s *Service) error { _, err := s.ReorderGroups(context.Background(), 1, []int{1, 1}); return err }, ErrInvalidTrack},
|
{"duplicate order", func(s *Service) error { _, err := s.ReorderGroups(context.Background(), 1, []int{1, 1}); return err }, ErrInvalidTrack},
|
||||||
|
{"negative upload group", func(s *Service) error {
|
||||||
|
return s.UploadTrack(context.Background(), 1, -1, "Track", "track.mp3", []byte("data"))
|
||||||
|
}, ErrInvalidTrack},
|
||||||
{"invalid assignment", func(s *Service) error { _, err := s.SetTrackGroup(context.Background(), 1, 2, -1); return err }, ErrInvalidTrack},
|
{"invalid assignment", func(s *Service) error { _, err := s.SetTrackGroup(context.Background(), 1, 2, -1); return err }, ErrInvalidTrack},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ func (s *Service) Delete(ctx context.Context, id int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UploadTrack stores an MP3 file and adds it to a playlist.
|
// UploadTrack stores an MP3 file and adds it to a playlist.
|
||||||
func (s *Service) UploadTrack(ctx context.Context, playlistID int, name, filename string, data []byte) error {
|
func (s *Service) UploadTrack(ctx context.Context, playlistID, groupID int, name, filename string, data []byte) error {
|
||||||
if playlistID <= 0 || strings.TrimSpace(name) == "" || len(data) == 0 || !strings.HasSuffix(strings.ToLower(filename), ".mp3") {
|
if playlistID <= 0 || groupID < 0 || strings.TrimSpace(name) == "" || len(data) == 0 || !strings.HasSuffix(strings.ToLower(filename), ".mp3") {
|
||||||
return ErrInvalidTrack
|
return ErrInvalidTrack
|
||||||
}
|
}
|
||||||
if _, err := s.repo.Get(ctx, playlistID); err != nil {
|
if _, err := s.repo.Get(ctx, playlistID); err != nil {
|
||||||
@@ -99,7 +99,7 @@ func (s *Service) UploadTrack(ctx context.Context, playlistID int, name, filenam
|
|||||||
if err != nil || fileType != "audio" || !strings.HasSuffix(strings.ToLower(stored), ".mp3") {
|
if err != nil || fileType != "audio" || !strings.HasSuffix(strings.ToLower(stored), ".mp3") {
|
||||||
return ErrInvalidTrack
|
return ErrInvalidTrack
|
||||||
}
|
}
|
||||||
if err := s.repo.AddTrack(ctx, playlistID, strings.TrimSpace(name), stored); err != nil {
|
if err := s.repo.AddTrack(ctx, playlistID, groupID, strings.TrimSpace(name), stored); err != nil {
|
||||||
_ = s.files.DeleteFile(ctx, stored)
|
_ = s.files.DeleteFile(ctx, stored)
|
||||||
return mapError(err)
|
return mapError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -3046,6 +3046,7 @@ type UploadPlaylistTrackReq struct {
|
|||||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
Filename string `protobuf:"bytes,3,opt,name=filename,proto3" json:"filename,omitempty"`
|
Filename string `protobuf:"bytes,3,opt,name=filename,proto3" json:"filename,omitempty"`
|
||||||
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
|
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
GroupID int32 `protobuf:"varint,5,opt,name=groupID,proto3" json:"groupID,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -3108,6 +3109,13 @@ func (x *UploadPlaylistTrackReq) GetData() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *UploadPlaylistTrackReq) GetGroupID() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.GroupID
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type UploadPlaylistTrackRsp struct {
|
type UploadPlaylistTrackRsp struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Playlist *Playlist `protobuf:"bytes,1,opt,name=playlist,proto3" json:"playlist,omitempty"`
|
Playlist *Playlist `protobuf:"bytes,1,opt,name=playlist,proto3" json:"playlist,omitempty"`
|
||||||
@@ -14077,14 +14085,15 @@ const file_main_proto_rawDesc = "" +
|
|||||||
"\bplaylist\x18\x01 \x01(\v2(.crabs.evening_detective_server.PlaylistR\bplaylist\"#\n" +
|
"\bplaylist\x18\x01 \x01(\v2(.crabs.evening_detective_server.PlaylistR\bplaylist\"#\n" +
|
||||||
"\x11DeletePlaylistReq\x12\x0e\n" +
|
"\x11DeletePlaylistReq\x12\x0e\n" +
|
||||||
"\x02ID\x18\x01 \x01(\x05R\x02ID\"\x13\n" +
|
"\x02ID\x18\x01 \x01(\x05R\x02ID\"\x13\n" +
|
||||||
"\x11DeletePlaylistRsp\"|\n" +
|
"\x11DeletePlaylistRsp\"\x96\x01\n" +
|
||||||
"\x16UploadPlaylistTrackReq\x12\x1e\n" +
|
"\x16UploadPlaylistTrackReq\x12\x1e\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"playlistID\x18\x01 \x01(\x05R\n" +
|
"playlistID\x18\x01 \x01(\x05R\n" +
|
||||||
"playlistID\x12\x12\n" +
|
"playlistID\x12\x12\n" +
|
||||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n" +
|
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n" +
|
||||||
"\bfilename\x18\x03 \x01(\tR\bfilename\x12\x12\n" +
|
"\bfilename\x18\x03 \x01(\tR\bfilename\x12\x12\n" +
|
||||||
"\x04data\x18\x04 \x01(\fR\x04data\"^\n" +
|
"\x04data\x18\x04 \x01(\fR\x04data\x12\x18\n" +
|
||||||
|
"\agroupID\x18\x05 \x01(\x05R\agroupID\"^\n" +
|
||||||
"\x16UploadPlaylistTrackRsp\x12D\n" +
|
"\x16UploadPlaylistTrackRsp\x12D\n" +
|
||||||
"\bplaylist\x18\x01 \x01(\v2(.crabs.evening_detective_server.PlaylistR\bplaylist\"V\n" +
|
"\bplaylist\x18\x01 \x01(\v2(.crabs.evening_detective_server.PlaylistR\bplaylist\"V\n" +
|
||||||
"\x18ReorderPlaylistTracksReq\x12\x1e\n" +
|
"\x18ReorderPlaylistTracksReq\x12\x1e\n" +
|
||||||
|
|||||||
Reference in New Issue
Block a user