From 33eba2aa9dd76e6a7f45c6ea77ebfa293a9daad1 Mon Sep 17 00:00:00 2001 From: skewb1k <101928759+skewb1k@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:39:34 +0300 Subject: [PATCH] player struct updated --- internal/controller/ws-handler.controller.go | 112 ++++++++++-------- internal/controller/ws-router.controller.go | 2 +- internal/repository/room/redis/player.go | 29 +++++ internal/service/helper.service.go | 22 +++- internal/service/member.service.go | 23 +++- internal/service/models.service.go | 19 +-- internal/service/player.service.go | 87 +++++++++----- internal/service/room.service.go | 19 +-- internal/service/service.go | 2 + internal/service/video.service.go | 115 ++++++++++++++++--- websocket-api.md | 66 ++++++----- 11 files changed, 350 insertions(+), 146 deletions(-) diff --git a/internal/controller/ws-handler.controller.go b/internal/controller/ws-handler.controller.go index d56a740..7c13c99 100644 --- a/internal/controller/ws-handler.controller.go +++ b/internal/controller/ws-handler.controller.go @@ -20,11 +20,12 @@ func (c controller) handleAlive(_ context.Context, _ *websocket.Conn, _ EmptyInp } type UpdatePlayerStateInput struct { - VideoId int `json:"video_id"` - IsPlaying bool `json:"is_playing"` - CurrentTime int `json:"current_time"` - PlaybackRate float64 `json:"playback_rate"` - UpdatedAt int `json:"updated_at"` + VideoId int `json:"video_id"` + IsPlaying bool `json:"is_playing"` + CurrentTime int `json:"current_time"` + PlaybackRate float64 `json:"playback_rate"` + UpdatedAt int `json:"updated_at"` + PlayerVersion int `json:"player_version"` } func (c controller) handleUpdatePlayerState(ctx context.Context, conn *websocket.Conn, input UpdatePlayerStateInput) error { @@ -32,14 +33,15 @@ func (c controller) handleUpdatePlayerState(ctx context.Context, conn *websocket memberId := c.getMemberIdFromCtx(ctx) updatePlayerStateResp, err := c.roomService.UpdatePlayerState(ctx, &service.UpdatePlayerStateParams{ - SenderConn: conn, - VideoId: input.VideoId, - IsPlaying: input.IsPlaying, - CurrentTime: input.CurrentTime, - PlaybackRate: input.PlaybackRate, - UpdatedAt: input.UpdatedAt, - SenderId: memberId, - RoomId: roomId, + SenderConn: conn, + VideoId: input.VideoId, + IsPlaying: input.IsPlaying, + CurrentTime: input.CurrentTime, + PlaybackRate: input.PlaybackRate, + UpdatedAt: input.UpdatedAt, + PlayerVersion: input.PlayerVersion, + SenderId: memberId, + RoomId: roomId, }) if err != nil { return fmt.Errorf("failed to update player state: %w", err) @@ -53,8 +55,10 @@ func (c controller) handleUpdatePlayerState(ctx context.Context, conn *websocket } type UpdatePlayerVideoInput struct { - VideoId int `json:"video_id"` - UpdatedAt int `json:"updated_at"` + VideoId int `json:"video_id"` + UpdatedAt int `json:"updated_at"` + PlayerVersion int `json:"player_version"` + PlaylistVersion int `json:"playlist_version"` } func (c controller) handleUpdatePlayerVideo(ctx context.Context, _ *websocket.Conn, input UpdatePlayerVideoInput) error { @@ -62,10 +66,12 @@ func (c controller) handleUpdatePlayerVideo(ctx context.Context, _ *websocket.Co memberId := c.getMemberIdFromCtx(ctx) updatePlayerVideoResp, err := c.roomService.UpdatePlayerVideo(ctx, &service.UpdatePlayerVideoParams{ - VideoId: input.VideoId, - UpdatedAt: input.UpdatedAt, - SenderId: memberId, - RoomId: roomId, + PlaylistVersion: input.PlaylistVersion, + PlayerVersion: input.PlayerVersion, + VideoId: input.VideoId, + UpdatedAt: input.UpdatedAt, + SenderId: memberId, + RoomId: roomId, }) if err != nil { return fmt.Errorf("failed to update player video: %w", err) @@ -84,8 +90,10 @@ func (c controller) handleUpdatePlayerVideo(ctx context.Context, _ *websocket.Co } type AddVideoInput struct { - VideoUrl string `json:"video_url"` - UpdatedAt int `json:"updated_at"` + VideoUrl string `json:"video_url"` + UpdatedAt int `json:"updated_at"` + PlaylsitVersion int `json:"playlist_version"` + PlayerVersion int `json:"player_version"` } func (c controller) handleAddVideo(ctx context.Context, _ *websocket.Conn, input AddVideoInput) error { @@ -93,10 +101,12 @@ func (c controller) handleAddVideo(ctx context.Context, _ *websocket.Conn, input memberId := c.getMemberIdFromCtx(ctx) addVideoResponse, err := c.roomService.AddVideo(ctx, &service.AddVideoParams{ - SenderId: memberId, - RoomId: roomId, - VideoUrl: input.VideoUrl, - UpdatedAt: input.UpdatedAt, + PlaylistVersion: input.PlaylsitVersion, + PlayerVersion: input.PlayerVersion, + SenderId: memberId, + RoomId: roomId, + VideoUrl: input.VideoUrl, + UpdatedAt: input.UpdatedAt, }) if err != nil { return fmt.Errorf("failed to add video: %w", err) @@ -126,33 +136,35 @@ func (c controller) handleAddVideo(ctx context.Context, _ *websocket.Conn, input return nil } -func (c controller) handleEndVideo(ctx context.Context, _ *websocket.Conn, _ EmptyInput) error { +type EndVideoInput struct { + PlayerVersion int `json:"player_version"` +} + +func (c controller) handleEndVideo(ctx context.Context, _ *websocket.Conn, input EndVideoInput) error { roomId := c.getRoomIdFromCtx(ctx) memberId := c.getMemberIdFromCtx(ctx) - addVideoResponse, err := c.roomService.EndVideo(ctx, &service.EndVideoParams{ - SenderId: memberId, - RoomId: roomId, + endVideoResponse, err := c.roomService.EndVideo(ctx, &service.EndVideoParams{ + PlayerVersion: input.PlayerVersion, + SenderId: memberId, + RoomId: roomId, }) if err != nil { - return fmt.Errorf("failed to add video: %w", err) + return fmt.Errorf("failed to end video: %w", err) } - if addVideoResponse.Player != nil { + if endVideoResponse.Playlist != nil { if err := c.broadcastPlayerVideoUpdated(ctx, - addVideoResponse.Conns, - addVideoResponse.Player, - addVideoResponse.Playlist, - addVideoResponse.Members, + endVideoResponse.Conns, + endVideoResponse.Player, + endVideoResponse.Playlist, + endVideoResponse.Members, ); err != nil { return fmt.Errorf("failed to broadcast player updated: %w", err) } } else { - if err := c.broadcast(ctx, addVideoResponse.Conns, &Output{ - Type: "VIDEO_ENDED", - Payload: nil, - }); err != nil { - return fmt.Errorf("failed to broadcast video ended: %w", err) + if err := c.broadcastPlayerStateUpdated(ctx, endVideoResponse.Conns, endVideoResponse.Player); err != nil { + return fmt.Errorf("failed to broadcast player state updated: %w", err) } } @@ -226,7 +238,8 @@ func (c controller) handlePromoteMember(ctx context.Context, _ *websocket.Conn, } type RemoveVideoInput struct { - VideoId int `json:"video_id"` + VideoId int `json:"video_id"` + PlaylistVersion int `json:"playlist_version"` } func (c controller) handleRemoveVideo(ctx context.Context, _ *websocket.Conn, input RemoveVideoInput) error { @@ -234,9 +247,10 @@ func (c controller) handleRemoveVideo(ctx context.Context, _ *websocket.Conn, in memberId := c.getMemberIdFromCtx(ctx) removeVideoResponse, err := c.roomService.RemoveVideo(ctx, &service.RemoveVideoParams{ - VideoId: input.VideoId, - SenderId: memberId, - RoomId: roomId, + PlaylistVersion: input.PlaylistVersion, + VideoId: input.VideoId, + SenderId: memberId, + RoomId: roomId, }) if err != nil { return fmt.Errorf("failed to remove video: %w", err) @@ -340,7 +354,8 @@ func (c controller) handleUpdateIsMuted(ctx context.Context, conn *websocket.Con } type ReorderPlaylistInput struct { - VideoIds []int `json:"video_ids"` + VideoIds []int `json:"video_ids"` + PlaylistVersion int `json:"playlist_version"` } func (c controller) handleReorderPlaylist(ctx context.Context, _ *websocket.Conn, input ReorderPlaylistInput) error { @@ -348,9 +363,10 @@ func (c controller) handleReorderPlaylist(ctx context.Context, _ *websocket.Conn memberId := c.getMemberIdFromCtx(ctx) removeVideoResponse, err := c.roomService.ReorderPlaylist(ctx, &service.ReorderPlaylistParams{ - VideoIds: input.VideoIds, - SenderId: memberId, - RoomId: roomId, + PlaylistVersion: input.PlaylistVersion, + VideoIds: input.VideoIds, + SenderId: memberId, + RoomId: roomId, }) if err != nil { return fmt.Errorf("failed to reorder playlist: %w", err) diff --git a/internal/controller/ws-router.controller.go b/internal/controller/ws-router.controller.go index b49a97f..242ddc1 100644 --- a/internal/controller/ws-router.controller.go +++ b/internal/controller/ws-router.controller.go @@ -23,7 +23,6 @@ func (c controller) getWSRouter() *wsrouter.WSRouter { // video wsrouter.Handle(mux, "ALIVE", c.handleAlive) wsrouter.Handle(mux, "ADD_VIDEO", c.handleAddVideo) - wsrouter.Handle(mux, "END_VIDEO", c.handleEndVideo) wsrouter.Handle(mux, "REMOVE_VIDEO", c.handleRemoveVideo) wsrouter.Handle(mux, "REORDER_PLAYLIST", c.handleReorderPlaylist) @@ -34,6 +33,7 @@ func (c controller) getWSRouter() *wsrouter.WSRouter { // player wsrouter.Handle(mux, "UPDATE_PLAYER_STATE", c.handleUpdatePlayerState) wsrouter.Handle(mux, "UPDATE_PLAYER_VIDEO", c.handleUpdatePlayerVideo) + wsrouter.Handle(mux, "END_VIDEO", c.handleEndVideo) // profile wsrouter.Handle(mux, "UPDATE_PROFILE", c.handleUpdateProfile) diff --git a/internal/repository/room/redis/player.go b/internal/repository/room/redis/player.go index f2b35ee..0013d2e 100644 --- a/internal/repository/room/redis/player.go +++ b/internal/repository/room/redis/player.go @@ -2,8 +2,10 @@ package redis import ( "context" + "errors" "fmt" + "github.com/redis/go-redis/v9" "github.com/sharetube/server/internal/repository/room" ) @@ -23,6 +25,33 @@ func (r repo) getVideoEndedKey(roomId string) string { return fmt.Sprintf("room:%s:video-ended", roomId) } +func (r repo) getPlayerVersionKey(roomId string) string { + return fmt.Sprintf("room:%s:player-version", roomId) +} + +func (r repo) IncrPlayerVersion(ctx context.Context, roomId string) (int, error) { + playerVersionKey := r.getPlayerVersionKey(roomId) + playerVersion, err := r.rc.Incr(ctx, playerVersionKey).Result() + if err != nil { + return 0, err + } + + return int(playerVersion), nil +} + +func (r repo) GetPlayerVersion(ctx context.Context, roomId string) (int, error) { + playerVersionKey := r.getPlayerVersionKey(roomId) + playerVersion, err := r.rc.Get(ctx, playerVersionKey).Int() + if err != nil { + if errors.Is(err, redis.Nil) { + return 0, nil + } + return 0, err + } + + return playerVersion, nil +} + func (r repo) SetVideoEnded(ctx context.Context, params *room.SetVideoEndedParams) error { // pipe := r.rc.TxPipeline() // pipe.Expire(ctx, videoEndedKey, r.maxExpireDuration) diff --git a/internal/service/helper.service.go b/internal/service/helper.service.go index 5b6779f..437bc9b 100644 --- a/internal/service/helper.service.go +++ b/internal/service/helper.service.go @@ -180,12 +180,26 @@ func (s service) updatePlayerVideo(ctx context.Context, roomId string, videoId i conns = append(conns, conn) } + playerVersion, err := s.roomRepo.IncrPlayerVersion(ctx, roomId) + if err != nil { + return nil, fmt.Errorf("failed to incr player version: %w", err) + } + + isEnded, err := s.roomRepo.GetVideoEnded(ctx, roomId) + if err != nil { + return nil, fmt.Errorf("failed to get video ended: %w", err) + } + return &updatePlayerVideoResponse{ Player: Player{ - CurrentTime: currentTime, - IsPlaying: isPlaying, - PlaybackRate: playbackRate, - UpdatedAt: updatedAt, + State: PlayerState{ + CurrentTime: currentTime, + IsPlaying: isPlaying, + PlaybackRate: playbackRate, + UpdatedAt: updatedAt, + }, + IsEnded: isEnded, + Version: playerVersion, }, Members: members, Playlist: Playlist{ diff --git a/internal/service/member.service.go b/internal/service/member.service.go index 8babf2a..9ecd463 100644 --- a/internal/service/member.service.go +++ b/internal/service/member.service.go @@ -518,6 +518,7 @@ func (s service) UpdateIsReady(ctx context.Context, params *UpdateIsReadyParams) player.IsPlaying = neededIsReady player.UpdatedAt = int(time.Now().UnixMicro()) + // todo: check isEnded if err := s.roomRepo.UpdatePlayerWaitingForReady(ctx, params.RoomId, !player.WaitingForReady); err != nil { return nil, fmt.Errorf("failed to update player waiting for ready: %w", err) } @@ -526,15 +527,29 @@ func (s service) UpdateIsReady(ctx context.Context, params *UpdateIsReadyParams) return nil, fmt.Errorf("failed to update player is playing: %w", err) } + playerVersion, err := s.roomRepo.IncrPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to incr player version: %w", err) + } + + isEnded, err := s.roomRepo.GetVideoEnded(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get video ended: %w", err) + } + return &UpdateIsReadyResponse{ Conns: conns, UpdatedMember: updatedMember, Members: members, Player: &Player{ - IsPlaying: player.IsPlaying, - CurrentTime: player.CurrentTime, - PlaybackRate: player.PlaybackRate, - UpdatedAt: player.UpdatedAt, + State: PlayerState{ + CurrentTime: player.CurrentTime, + IsPlaying: player.IsPlaying, + PlaybackRate: player.PlaybackRate, + UpdatedAt: player.UpdatedAt, + }, + IsEnded: isEnded, + Version: playerVersion, }, }, nil } diff --git a/internal/service/models.service.go b/internal/service/models.service.go index a2f01b8..b31aff6 100644 --- a/internal/service/models.service.go +++ b/internal/service/models.service.go @@ -25,17 +25,22 @@ type Playlist struct { Version int `json:"version"` } -type Player struct { - IsPlaying bool `json:"is_playing"` +type PlayerState struct { CurrentTime int `json:"current_time"` + IsPlaying bool `json:"is_playing"` PlaybackRate float64 `json:"playback_rate"` UpdatedAt int `json:"updated_at"` } +type Player struct { + State PlayerState `json:"state"` + IsEnded bool `json:"is_ended"` + Version int `json:"version"` +} + type Room struct { - Id string `json:"id"` - Player Player `json:"player"` - VideoEnded bool `json:"video_ended"` - Members []Member `json:"members"` - Playlist Playlist `json:"playlist"` + Id string `json:"id"` + Player Player `json:"player"` + Members []Member `json:"members"` + Playlist Playlist `json:"playlist"` } diff --git a/internal/service/player.service.go b/internal/service/player.service.go index 8ea0593..26715e8 100644 --- a/internal/service/player.service.go +++ b/internal/service/player.service.go @@ -7,17 +7,19 @@ import ( validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/gorilla/websocket" + "github.com/sharetube/server/internal/repository/room" ) type UpdatePlayerStateParams struct { - VideoId int `json:"video_id"` - IsPlaying bool `json:"is_playing"` - CurrentTime int `json:"current_time"` - PlaybackRate float64 `json:"playback_rate"` - UpdatedAt int `json:"updated_at"` - SenderId string `json:"sender_id"` - SenderConn *websocket.Conn `json:"sender_conn"` - RoomId string `json:"room_id"` + VideoId int `json:"video_id"` + IsPlaying bool `json:"is_playing"` + CurrentTime int `json:"current_time"` + PlaybackRate float64 `json:"playback_rate"` + UpdatedAt int `json:"updated_at"` + PlayerVersion int `json:"player_version"` + SenderId string `json:"sender_id"` + SenderConn *websocket.Conn `json:"sender_conn"` + RoomId string `json:"room_id"` } type UpdatePlayerStateResponse struct { @@ -31,6 +33,15 @@ func (s service) UpdatePlayerState(ctx context.Context, params *UpdatePlayerStat } //? add validation + playerVersion, err := s.roomRepo.GetPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get player version: %w", err) + } + + if playerVersion != params.PlayerVersion { + return nil, ErrPlayerVersionMismatch + } + currentVideoId, err := s.roomRepo.GetCurrentVideoId(ctx, params.RoomId) if err != nil { return nil, fmt.Errorf("failed to get current video id: %w", err) @@ -68,20 +79,24 @@ func (s service) UpdatePlayerState(ctx context.Context, params *UpdatePlayerStat } } - // if player.IsEnded != params.IsEnded { - // updated = true - // if err := s.roomRepo.UpdatePlayerIsEnded(ctx, params.RoomId, params.IsEnded); err != nil { - // return UpdatePlayerStateResponse{}, fmt.Errorf("failed to update player is ended: %w", err) - // } - // } + if err := s.roomRepo.SetVideoEnded(ctx, &room.SetVideoEndedParams{ + RoomId: params.RoomId, + VideoEnded: false, + }); err != nil { + return nil, fmt.Errorf("failed to set video ended: %w", err) + } if !updated { return &UpdatePlayerStateResponse{ Player: Player{ - IsPlaying: player.IsPlaying, - CurrentTime: player.CurrentTime, - PlaybackRate: player.PlaybackRate, - UpdatedAt: player.UpdatedAt, + State: PlayerState{ + IsPlaying: player.IsPlaying, + CurrentTime: player.CurrentTime, + PlaybackRate: player.PlaybackRate, + UpdatedAt: player.UpdatedAt, + }, + IsEnded: false, + Version: playerVersion, }, Conns: []*websocket.Conn{params.SenderConn}, }, nil @@ -114,22 +129,33 @@ func (s service) UpdatePlayerState(ctx context.Context, params *UpdatePlayerStat return nil, fmt.Errorf("failed to get conns from member ids: %w", err) } + playerVersion, err = s.roomRepo.IncrPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to incr player version: %w", err) + } + return &UpdatePlayerStateResponse{ Player: Player{ - IsPlaying: params.IsPlaying, - CurrentTime: params.CurrentTime, - PlaybackRate: params.PlaybackRate, - UpdatedAt: params.UpdatedAt, + State: PlayerState{ + IsPlaying: params.IsPlaying, + CurrentTime: params.CurrentTime, + PlaybackRate: params.PlaybackRate, + UpdatedAt: params.UpdatedAt, + }, + IsEnded: false, + Version: playerVersion, }, Conns: conns, }, nil } type UpdatePlayerVideoParams struct { - VideoId int `json:"video_id"` - UpdatedAt int `json:"updated_at"` - SenderId string `json:"sender_id"` - RoomId string `json:"room_id"` + VideoId int `json:"video_id"` + UpdatedAt int `json:"updated_at"` + SenderId string `json:"sender_id"` + RoomId string `json:"room_id"` + PlayerVersion int `json:"player_version"` + PlaylistVersion int `json:"playlist_version"` } type UpdatePlayerVideoResponse struct { @@ -150,6 +176,15 @@ func (s service) UpdatePlayerVideo(ctx context.Context, params *UpdatePlayerVide return nil, err } + playerVersion, err := s.roomRepo.GetPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get player version: %w", err) + } + + if playerVersion != params.PlayerVersion { + return nil, errors.New("player version is not equal") + } + updatePlayerVideoRes, err := s.updatePlayerVideo(ctx, params.RoomId, params.VideoId, params.UpdatedAt) if err != nil { return nil, err diff --git a/internal/service/room.service.go b/internal/service/room.service.go index beb5979..6315350 100644 --- a/internal/service/room.service.go +++ b/internal/service/room.service.go @@ -306,11 +306,6 @@ func (s service) JoinRoom(ctx context.Context, params *JoinRoomParams) (*JoinRoo } func (s service) GetRoom(ctx context.Context, roomId string) (*Room, error) { - player, err := s.roomRepo.GetPlayer(ctx, roomId) - if err != nil { - return nil, fmt.Errorf("failed to get player: %w", err) - } - members, err := s.getMembers(ctx, roomId) if err != nil { return nil, fmt.Errorf("failed to get members: %w", err) @@ -321,20 +316,14 @@ func (s service) GetRoom(ctx context.Context, roomId string) (*Room, error) { return nil, fmt.Errorf("failed to get playlist: %w", err) } - videoEnded, err := s.roomRepo.GetVideoEnded(ctx, roomId) + player, err := s.getPlayer(ctx, roomId) if err != nil { - return nil, fmt.Errorf("failed to get video ended: %w", err) + return nil, fmt.Errorf("failed to get player: %w", err) } return &Room{ - Id: roomId, - VideoEnded: videoEnded, - Player: Player{ - IsPlaying: player.IsPlaying, - CurrentTime: player.CurrentTime, - PlaybackRate: player.PlaybackRate, - UpdatedAt: player.UpdatedAt, - }, + Id: roomId, + Player: *player, Members: members, Playlist: *playlist, }, nil diff --git a/internal/service/service.go b/internal/service/service.go index fecb4ce..ec33d6a 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -55,6 +55,8 @@ type iRoomRepo interface { // player SetPlayer(context.Context, *room.SetPlayerParams) error GetPlayer(context.Context, string) (room.Player, error) + GetPlayerVersion(context.Context, string) (int, error) + IncrPlayerVersion(context.Context, string) (int, error) IsPlayerExists(context.Context, string) (bool, error) RemovePlayer(context.Context, string) error ExpirePlayer(context.Context, *room.ExpirePlayerParams) error diff --git a/internal/service/video.service.go b/internal/service/video.service.go index a12a8e3..c3dc2a6 100644 --- a/internal/service/video.service.go +++ b/internal/service/video.service.go @@ -12,6 +12,11 @@ import ( "github.com/sharetube/server/pkg/ytvideodata" ) +var ( + ErrPlayerVersionMismatch = errors.New("player version mismatch") + ErrPlaylistVersionMismatch = errors.New("playlist version mismatch") +) + func (s service) getVideos(ctx context.Context, roomId string) ([]Video, error) { videosIds, err := s.roomRepo.GetVideoIds(ctx, roomId) if err != nil { @@ -69,6 +74,34 @@ func (s service) getPlaylist(ctx context.Context, roomId string) (*Playlist, err }, nil } +func (s service) getPlayer(ctx context.Context, roomId string) (*Player, error) { + player, err := s.roomRepo.GetPlayer(ctx, roomId) + if err != nil { + return nil, fmt.Errorf("failed to get player: %w", err) + } + + videoEnded, err := s.roomRepo.GetVideoEnded(ctx, roomId) + if err != nil { + return nil, fmt.Errorf("failed to get video ended: %w", err) + } + + playerVersion, err := s.roomRepo.GetPlayerVersion(ctx, roomId) + if err != nil { + return nil, fmt.Errorf("failed to get player version: %w", err) + } + + return &Player{ + State: PlayerState{ + CurrentTime: player.CurrentTime, + IsPlaying: player.IsPlaying, + PlaybackRate: player.PlaybackRate, + UpdatedAt: player.UpdatedAt, + }, + IsEnded: videoEnded, + Version: playerVersion, + }, nil +} + func (s service) getPlaylistWithIncrVersion(ctx context.Context, roomId string) (*Playlist, error) { playlistVersion, err := s.roomRepo.IncrPlaylistVersion(ctx, roomId) if err != nil { @@ -155,10 +188,12 @@ func (s service) getLastVideo(ctx context.Context, roomId string) (*Video, error } type AddVideoParams struct { - SenderId string `json:"sender_id"` - RoomId string `json:"room_id"` - VideoUrl string `json:"video_url"` - UpdatedAt int `json:"updated_at"` + SenderId string `json:"sender_id"` + RoomId string `json:"room_id"` + VideoUrl string `json:"video_url"` + UpdatedAt int `json:"updated_at"` + PlaylistVersion int `json:"playlist_version"` + PlayerVersion int `json:"player_version"` } // todo: two optional responses @@ -181,6 +216,24 @@ func (s service) AddVideo(ctx context.Context, params *AddVideoParams) (*AddVide return nil, err } + playerVersion, err := s.roomRepo.GetPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get player version: %w", err) + } + + if params.PlayerVersion != playerVersion { + return nil, ErrPlayerVersionMismatch + } + + playlistVersion, err := s.roomRepo.GetPlaylistVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get playlist version: %w", err) + } + + if params.PlaylistVersion != playlistVersion { + return nil, ErrPlaylistVersionMismatch + } + videoData, err := ytvideodata.Get(params.VideoUrl) if err != nil { return nil, fmt.Errorf("failed to get video data: %w", err) @@ -256,8 +309,9 @@ func (s service) AddVideo(ctx context.Context, params *AddVideoParams) (*AddVide } type EndVideoParams struct { - SenderId string `json:"sender_id"` - RoomId string `json:"room_id"` + SenderId string `json:"sender_id"` + RoomId string `json:"room_id"` + PlayerVersion int `json:"player_version"` } type EndVideoResponse struct { @@ -272,6 +326,15 @@ func (s service) EndVideo(ctx context.Context, params *EndVideoParams) (*EndVide return nil, err } + playerVersion, err := s.roomRepo.GetPlayerVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get player version: %w", err) + } + + if playerVersion != params.PlayerVersion { + return nil, errors.New("player version is not equal") + } + videoEnded, err := s.roomRepo.GetVideoEnded(ctx, params.RoomId) if err != nil { return nil, fmt.Errorf("failed to get video ended: %w", err) @@ -312,15 +375,22 @@ func (s service) EndVideo(ctx context.Context, params *EndVideoParams) (*EndVide return nil, fmt.Errorf("failed to get conns: %w", err) } + player, err := s.getPlayer(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get player: %w", err) + } + return &EndVideoResponse{ - Conns: conns, + Player: player, + Conns: conns, }, nil } type RemoveVideoParams struct { - SenderId string `json:"sender_id"` - VideoId int `json:"video_id"` - RoomId string `json:"room_id"` + SenderId string `json:"sender_id"` + VideoId int `json:"video_id"` + RoomId string `json:"room_id"` + PlaylistVersion int `json:"playlist_version"` } type RemoveVideoResponse struct { @@ -340,6 +410,15 @@ func (s service) RemoveVideo(ctx context.Context, params *RemoveVideoParams) (*R return nil, err } + playlistVersion, err := s.roomRepo.GetPlaylistVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get playlist version: %w", err) + } + + if params.PlaylistVersion != playlistVersion { + return nil, ErrPlaylistVersionMismatch + } + if err := s.roomRepo.RemoveVideoFromList(ctx, &room.RemoveVideoFromListParams{ VideoId: params.VideoId, RoomId: params.RoomId, @@ -372,9 +451,10 @@ func (s service) RemoveVideo(ctx context.Context, params *RemoveVideoParams) (*R } type ReorderPlaylistParams struct { - VideoIds []int `json:"video_ids"` - SenderId string `json:"sender_id"` - RoomId string `json:"room_id"` + VideoIds []int `json:"video_ids"` + SenderId string `json:"sender_id"` + RoomId string `json:"room_id"` + PlaylistVersion int `json:"playlist_version"` } type ReorderPlaylistResponse struct { @@ -393,6 +473,15 @@ func (s service) ReorderPlaylist(ctx context.Context, params *ReorderPlaylistPar return nil, err } + playlistVersion, err := s.roomRepo.GetPlaylistVersion(ctx, params.RoomId) + if err != nil { + return nil, fmt.Errorf("failed to get playlist version: %w", err) + } + + if params.PlaylistVersion != playlistVersion { + return nil, ErrPlaylistVersionMismatch + } + if err := s.roomRepo.ReorderList(ctx, &room.ReorderListParams{ VideoIds: params.VideoIds, RoomId: params.RoomId, diff --git a/websocket-api.md b/websocket-api.md index 220a082..4ae26f9 100644 --- a/websocket-api.md +++ b/websocket-api.md @@ -83,7 +83,9 @@ null ```json { "video_url": "[string]", - "updated_at": "[number]" + "updated_at": "[number]", + "playlist_version":"[number]", + "player_version":"[number]" } ``` @@ -95,7 +97,8 @@ null ```json { - "video_id": "[number]" + "video_id": "[number]", + "playlist_version":"[number]" } ``` @@ -110,6 +113,7 @@ null "video_ids": [ "[number]" ], + "playlist_version":"[number]" } ``` @@ -146,10 +150,11 @@ null ```json { "video_id": "[number]", + "player_version":"[number]", "playback_rate": "[number]", "is_playing": "[boolean]", "current_time": "[number]", - "updated_at": "[number]" + "updated_at": "[number]", } ``` @@ -160,7 +165,9 @@ null