Feat: room can join multiple client

pull/39/head
zijiren233 3 years ago
parent 56b599d0c5
commit 8147854f35

@ -3,9 +3,6 @@ package op
import (
"sync"
"time"
"github.com/synctv-org/synctv/internal/model"
pb "github.com/synctv-org/synctv/proto/message"
)
type current struct {
@ -89,47 +86,6 @@ func (c *current) SetSeekRate(seek, rate, timeDiff float64) Status {
return c.current.SetSeekRate(seek, rate, timeDiff)
}
func (c *Current) Proto() *pb.Current {
current := &pb.Current{
Status: &pb.Status{
Seek: c.Status.Seek,
Rate: c.Status.Rate,
Playing: c.Status.Playing,
},
}
current.Movie = &pb.MovieInfo{
Id: c.Movie.Movie.ID,
Base: &pb.BaseMovieInfo{
Url: c.Movie.Movie.Base.Url,
Name: c.Movie.Movie.Base.Name,
Live: c.Movie.Movie.Base.Live,
Proxy: c.Movie.Movie.Base.Proxy,
RtmpSource: c.Movie.Movie.Base.RtmpSource,
Type: c.Movie.Movie.Base.Type,
Headers: c.Movie.Movie.Base.Headers,
},
CreatedAt: c.Movie.Movie.CreatedAt.UnixMilli(),
Creator: GetUserName(c.Movie.Movie.CreatorID),
}
if c.Movie.Movie.Base.VendorInfo.Vendor != "" {
current.Movie.Base.VendorInfo = &pb.VendorInfo{
Vendor: string(c.Movie.Movie.Base.VendorInfo.Vendor),
Shared: c.Movie.Movie.Base.VendorInfo.Shared,
}
switch c.Movie.Movie.Base.VendorInfo.Vendor {
case model.StreamingVendorBilibili:
current.Movie.Base.VendorInfo.Bilibili = &pb.BilibiliVendorInfo{
Bvid: c.Movie.Movie.Base.VendorInfo.Bilibili.Bvid,
Cid: c.Movie.Movie.Base.VendorInfo.Bilibili.Cid,
Epid: c.Movie.Movie.Base.VendorInfo.Bilibili.Epid,
Quality: c.Movie.Movie.Base.VendorInfo.Bilibili.Quality,
VendorName: c.Movie.Movie.Base.VendorInfo.Bilibili.VendorName,
}
}
}
return current
}
func (c *Current) UpdateSeek() {
if c.Movie.Movie.Base.Live {
c.Status.lastUpdate = time.Now()

@ -16,7 +16,7 @@ import (
type Hub struct {
id string
clients rwmap.RWMap[string, *Client]
clients rwmap.RWMap[string, *rwmap.RWMap[*Client, struct{}]]
broadcast chan *broadcastMessage
exit chan struct{}
closed uint32
@ -26,29 +26,22 @@ type Hub struct {
}
type broadcastMessage struct {
data Message
sender string
sendToSelf bool
ignoreId []string
data Message
ignoreClient []*Client
ignoreId []string
}
type BroadcastConf func(*broadcastMessage)
func WithSender(sender string) BroadcastConf {
func WithIgnoreClient(cli ...*Client) BroadcastConf {
return func(bm *broadcastMessage) {
bm.sender = sender
}
}
func WithSendToSelf() BroadcastConf {
return func(bm *broadcastMessage) {
bm.sendToSelf = true
bm.ignoreClient = cli
}
}
func WithIgnoreId(id ...string) BroadcastConf {
return func(bm *broadcastMessage) {
bm.ignoreId = append(bm.ignoreId, id...)
bm.ignoreId = id
}
}
@ -73,19 +66,21 @@ func (h *Hub) serve() error {
select {
case message := <-h.broadcast:
h.devMessage(message.data)
h.clients.Range(func(_ string, cli *Client) bool {
if !message.sendToSelf {
if cli.u.Username == message.sender {
h.clients.Range(func(id string, cli *rwmap.RWMap[*Client, struct{}]) bool {
cli.Range(func(c *Client, value struct{}) bool {
if utils.In(message.ignoreId, c.u.ID) {
return true
}
}
if utils.In(message.ignoreId, cli.u.Username) {
if utils.In(message.ignoreClient, c) {
return true
}
if err := c.Send(message.data); err != nil {
log.Debugf("hub: %s, write to client err: %s\nmessage: %+v", h.id, err, message)
c.Close()
}
return true
}
if err := cli.Send(message.data); err != nil {
log.Debugf("hub: %s, write to client err: %s\nmessage: %+v", h.id, err, message)
cli.Close()
}
})
return true
})
case <-h.exit:
@ -98,17 +93,18 @@ func (h *Hub) serve() error {
func (h *Hub) ping() {
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
var pre int64 = 0
var (
pre int64 = 0
current int64
)
for {
select {
case <-ticker.C:
current := h.ClientNum()
current = h.PeopleNum()
if current != pre {
if err := h.Broadcast(&ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_PEOPLE,
PeopleNum: current,
},
Type: pb.ElementMessageType_CHANGE_PEOPLE,
PeopleNum: current,
}); err != nil {
continue
}
@ -144,9 +140,13 @@ func (h *Hub) Close() error {
return ErrAlreadyClosed
}
close(h.exit)
h.clients.Range(func(_ string, client *Client) bool {
h.clients.Delete(client.u.ID)
client.Close()
h.clients.Range(func(id string, client *rwmap.RWMap[*Client, struct{}]) bool {
h.clients.Delete(id)
client.Range(func(key *Client, value struct{}) bool {
client.Delete(key)
key.Close()
return true
})
return true
})
h.wg.Wait()
@ -173,40 +173,54 @@ func (h *Hub) Broadcast(data Message, conf ...BroadcastConf) error {
}
}
func (h *Hub) RegClient(cli *Client) (*Client, error) {
func (h *Hub) RegClient(cli *Client) error {
if h.Closed() {
return nil, ErrAlreadyClosed
return ErrAlreadyClosed
}
err := h.Start()
if err != nil {
return nil, err
return err
}
c, loaded := h.clients.LoadOrStore(cli.u.ID, cli)
c, _ := h.clients.LoadOrStore(cli.u.ID, &rwmap.RWMap[*Client, struct{}]{})
_, loaded := c.LoadOrStore(cli, struct{}{})
if loaded {
return nil, errors.New("client already registered")
return errors.New("client already exist")
}
return c, nil
return nil
}
func (h *Hub) UnRegClient(user *User) error {
func (h *Hub) UnRegClient(cli *Client) error {
if h.Closed() {
return ErrAlreadyClosed
}
if user == nil {
if cli == nil {
return errors.New("user is nil")
}
_, loaded := h.clients.LoadAndDelete(user.ID)
c, loaded := h.clients.Load(cli.u.ID)
if !loaded {
return errors.New("client not found")
}
_, loaded2 := c.LoadAndDelete(cli)
if !loaded2 {
return errors.New("client not found")
}
if c.Len() == 0 {
if h.clients.CompareAndDelete(cli.u.ID, c) {
c.Range(func(key *Client, value struct{}) bool {
c.Delete(key)
h.RegClient(key)
return true
})
}
}
return nil
}
func (h *Hub) ClientNum() int64 {
func (h *Hub) PeopleNum() int64 {
return h.clients.Len()
}
func (h *Hub) SendToUser(userID string, data Message) error {
func (h *Hub) SendToUser(userID string, data Message) (err error) {
if h.Closed() {
return ErrAlreadyClosed
}
@ -214,5 +228,17 @@ func (h *Hub) SendToUser(userID string, data Message) error {
if !ok {
return nil
}
return cli.Send(data)
cli.Range(func(key *Client, value struct{}) bool {
if err = key.Send(data); err != nil {
cli.CompareAndDelete(key, value)
log.Debugf("hub: %s, write to client err: %s\nmessage: %+v", h.id, err, data)
key.Close()
}
return true
})
return
}
func (h *Hub) LoadClient(userID string) (*rwmap.RWMap[*Client, struct{}], bool) {
return h.clients.Load(userID)
}

@ -3,8 +3,6 @@ package op
import (
"io"
json "github.com/json-iterator/go"
"github.com/gorilla/websocket"
pb "github.com/synctv-org/synctv/proto/message"
"google.golang.org/protobuf/proto"
@ -14,48 +12,20 @@ type Message interface {
MessageType() int
String() string
Encode(w io.Writer) error
BeforeSend(sendTo *User) error
}
type ElementJsonMessage struct {
BeforeSendFunc func(sendTo *User) error
*pb.ElementMessage
}
func (em *ElementJsonMessage) MessageType() int {
return websocket.TextMessage
}
func (em *ElementJsonMessage) String() string {
return em.ElementMessage.String()
}
func (em *ElementJsonMessage) Encode(w io.Writer) error {
return json.NewEncoder(w).Encode(em)
}
func (em *ElementJsonMessage) BeforeSend(sendTo *User) error {
if em.BeforeSendFunc != nil {
return em.BeforeSendFunc(sendTo)
}
return nil
}
type ElementMessage struct {
BeforeSendFunc func(sendTo *User) error
*pb.ElementMessage
}
type ElementMessage pb.ElementMessage
func (em *ElementMessage) MessageType() int {
return websocket.BinaryMessage
}
func (em *ElementMessage) String() string {
return em.ElementMessage.String()
return (*pb.ElementMessage)(em).String()
}
func (em *ElementMessage) Encode(w io.Writer) error {
b, err := proto.Marshal(em)
b, err := proto.Marshal((*pb.ElementMessage)(em))
if err != nil {
return err
}
@ -63,13 +33,6 @@ func (em *ElementMessage) Encode(w io.Writer) error {
return err
}
func (em *ElementMessage) BeforeSend(sendTo *User) error {
if em.BeforeSendFunc != nil {
return em.BeforeSendFunc(sendTo)
}
return nil
}
type PingMessage struct{}
func (pm *PingMessage) MessageType() int {
@ -83,7 +46,3 @@ func (pm *PingMessage) String() string {
func (pm *PingMessage) Encode(w io.Writer) error {
return nil
}
func (pm *PingMessage) BeforeSend(sendTo *User) error {
return nil
}

@ -9,6 +9,7 @@ import (
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/gencontainer/rwmap"
rtmps "github.com/zijiren233/livelib/server"
"github.com/zijiren233/stream"
"golang.org/x/crypto/bcrypt"
@ -29,11 +30,11 @@ func (r *Room) lazyInitHub() {
})
}
func (r *Room) ClientNum() int64 {
func (r *Room) PeopleNum() int64 {
if r.hub == nil {
return 0
}
return r.hub.ClientNum()
return r.hub.PeopleNum()
}
func (r *Room) Broadcast(data Message, conf ...BroadcastConf) error {
@ -43,6 +44,13 @@ func (r *Room) Broadcast(data Message, conf ...BroadcastConf) error {
return r.hub.Broadcast(data, conf...)
}
func (r *Room) LoadClient(userID string) (*rwmap.RWMap[*Client, struct{}], bool) {
if r.hub == nil {
return nil, false
}
return r.hub.LoadClient(userID)
}
func (r *Room) SendToUser(user *User, data Message) error {
if r.hub == nil {
return nil
@ -201,14 +209,24 @@ func (r *Room) GetMoviesWithPage(page, pageSize int) []*Movie {
return r.movies.GetMoviesWithPage(page, pageSize)
}
func (r *Room) RegClient(user *User, conn *websocket.Conn) (*Client, error) {
func (r *Room) NewClient(user *User, conn *websocket.Conn) (*Client, error) {
r.lazyInitHub()
cli := newClient(user, r, conn)
err := r.hub.RegClient(cli)
if err != nil {
return nil, err
}
return cli, nil
}
func (r *Room) RegClient(cli *Client) error {
r.lazyInitHub()
return r.hub.RegClient(newClient(user, r, conn))
return r.hub.RegClient(cli)
}
func (r *Room) UnregisterClient(user *User) error {
func (r *Room) UnregisterClient(cli *Client) error {
r.lazyInitHub()
return r.hub.UnRegClient(user)
return r.hub.UnRegClient(cli)
}
func (r *Room) SetStatus(playing bool, seek float64, rate float64, timeDiff float64) Status {

@ -114,10 +114,10 @@ func LoadOrInitRoomByID(id string) (*Room, error) {
return LoadOrInitRoom(room)
}
func ClientNum(roomID string) int64 {
func PeopleNum(roomID string) int64 {
r, loaded := roomCache.Load(roomID)
if loaded {
return r.Value().ClientNum()
return r.Value().PeopleNum()
}
return 0
}
@ -189,7 +189,7 @@ func GetRoomHeapInCacheWithoutHidden() []*RoomInfo {
rooms.Push(&RoomInfo{
RoomId: v.ID,
RoomName: v.Name,
PeopleNum: v.ClientNum(),
PeopleNum: v.PeopleNum(),
NeedPassword: v.NeedPassword(),
Creator: GetUserName(v.CreatorID),
CreatedAt: v.CreatedAt.UnixMilli(),

@ -99,180 +99,6 @@ func (ElementMessageType) EnumDescriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{0}
}
type BaseMovieInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Live bool `protobuf:"varint,3,opt,name=live,proto3" json:"live,omitempty"`
Proxy bool `protobuf:"varint,4,opt,name=proxy,proto3" json:"proxy,omitempty"`
RtmpSource bool `protobuf:"varint,5,opt,name=rtmpSource,proto3" json:"rtmpSource,omitempty"`
Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"`
Headers map[string]string `protobuf:"bytes,7,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
VendorInfo *VendorInfo `protobuf:"bytes,8,opt,name=vendorInfo,proto3" json:"vendorInfo,omitempty"`
}
func (x *BaseMovieInfo) Reset() {
*x = BaseMovieInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BaseMovieInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BaseMovieInfo) ProtoMessage() {}
func (x *BaseMovieInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BaseMovieInfo.ProtoReflect.Descriptor instead.
func (*BaseMovieInfo) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{0}
}
func (x *BaseMovieInfo) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *BaseMovieInfo) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *BaseMovieInfo) GetLive() bool {
if x != nil {
return x.Live
}
return false
}
func (x *BaseMovieInfo) GetProxy() bool {
if x != nil {
return x.Proxy
}
return false
}
func (x *BaseMovieInfo) GetRtmpSource() bool {
if x != nil {
return x.RtmpSource
}
return false
}
func (x *BaseMovieInfo) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *BaseMovieInfo) GetHeaders() map[string]string {
if x != nil {
return x.Headers
}
return nil
}
func (x *BaseMovieInfo) GetVendorInfo() *VendorInfo {
if x != nil {
return x.VendorInfo
}
return nil
}
type MovieInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Base *BaseMovieInfo `protobuf:"bytes,2,opt,name=base,proto3" json:"base,omitempty"`
CreatedAt int64 `protobuf:"varint,3,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
Creator string `protobuf:"bytes,4,opt,name=creator,proto3" json:"creator,omitempty"`
}
func (x *MovieInfo) Reset() {
*x = MovieInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MovieInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MovieInfo) ProtoMessage() {}
func (x *MovieInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MovieInfo.ProtoReflect.Descriptor instead.
func (*MovieInfo) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{1}
}
func (x *MovieInfo) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *MovieInfo) GetBase() *BaseMovieInfo {
if x != nil {
return x.Base
}
return nil
}
func (x *MovieInfo) GetCreatedAt() int64 {
if x != nil {
return x.CreatedAt
}
return 0
}
func (x *MovieInfo) GetCreator() string {
if x != nil {
return x.Creator
}
return ""
}
type Status struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -286,7 +112,7 @@ type Status struct {
func (x *Status) Reset() {
*x = Status{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[2]
mi := &file_proto_message_message_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -299,7 +125,7 @@ func (x *Status) String() string {
func (*Status) ProtoMessage() {}
func (x *Status) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[2]
mi := &file_proto_message_message_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -312,7 +138,7 @@ func (x *Status) ProtoReflect() protoreflect.Message {
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
func (*Status) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{2}
return file_proto_message_message_proto_rawDescGZIP(), []int{0}
}
func (x *Status) GetSeek() float64 {
@ -336,203 +162,6 @@ func (x *Status) GetPlaying() bool {
return false
}
type Current struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Movie *MovieInfo `protobuf:"bytes,1,opt,name=movie,proto3" json:"movie,omitempty"`
Status *Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
}
func (x *Current) Reset() {
*x = Current{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Current) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Current) ProtoMessage() {}
func (x *Current) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Current.ProtoReflect.Descriptor instead.
func (*Current) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{3}
}
func (x *Current) GetMovie() *MovieInfo {
if x != nil {
return x.Movie
}
return nil
}
func (x *Current) GetStatus() *Status {
if x != nil {
return x.Status
}
return nil
}
type VendorInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Vendor string `protobuf:"bytes,1,opt,name=vendor,proto3" json:"vendor,omitempty"`
Shared bool `protobuf:"varint,2,opt,name=shared,proto3" json:"shared,omitempty"`
Bilibili *BilibiliVendorInfo `protobuf:"bytes,3,opt,name=bilibili,proto3,oneof" json:"bilibili,omitempty"`
}
func (x *VendorInfo) Reset() {
*x = VendorInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VendorInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VendorInfo) ProtoMessage() {}
func (x *VendorInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VendorInfo.ProtoReflect.Descriptor instead.
func (*VendorInfo) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{4}
}
func (x *VendorInfo) GetVendor() string {
if x != nil {
return x.Vendor
}
return ""
}
func (x *VendorInfo) GetShared() bool {
if x != nil {
return x.Shared
}
return false
}
func (x *VendorInfo) GetBilibili() *BilibiliVendorInfo {
if x != nil {
return x.Bilibili
}
return nil
}
type BilibiliVendorInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bvid string `protobuf:"bytes,1,opt,name=bvid,proto3" json:"bvid,omitempty"`
Cid uint64 `protobuf:"varint,2,opt,name=cid,proto3" json:"cid,omitempty"`
Epid uint64 `protobuf:"varint,3,opt,name=epid,proto3" json:"epid,omitempty"`
Quality uint64 `protobuf:"varint,4,opt,name=quality,proto3" json:"quality,omitempty"`
VendorName string `protobuf:"bytes,5,opt,name=vendorName,proto3" json:"vendorName,omitempty"`
}
func (x *BilibiliVendorInfo) Reset() {
*x = BilibiliVendorInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BilibiliVendorInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BilibiliVendorInfo) ProtoMessage() {}
func (x *BilibiliVendorInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BilibiliVendorInfo.ProtoReflect.Descriptor instead.
func (*BilibiliVendorInfo) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{5}
}
func (x *BilibiliVendorInfo) GetBvid() string {
if x != nil {
return x.Bvid
}
return ""
}
func (x *BilibiliVendorInfo) GetCid() uint64 {
if x != nil {
return x.Cid
}
return 0
}
func (x *BilibiliVendorInfo) GetEpid() uint64 {
if x != nil {
return x.Epid
}
return 0
}
func (x *BilibiliVendorInfo) GetQuality() uint64 {
if x != nil {
return x.Quality
}
return 0
}
func (x *BilibiliVendorInfo) GetVendorName() string {
if x != nil {
return x.VendorName
}
return ""
}
type ElementMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -543,15 +172,14 @@ type ElementMessage struct {
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Rate float64 `protobuf:"fixed64,4,opt,name=rate,proto3" json:"rate,omitempty"`
Seek float64 `protobuf:"fixed64,5,opt,name=seek,proto3" json:"seek,omitempty"`
Current *Current `protobuf:"bytes,6,opt,name=current,proto3,oneof" json:"current,omitempty"`
PeopleNum int64 `protobuf:"varint,7,opt,name=peopleNum,proto3" json:"peopleNum,omitempty"`
Time int64 `protobuf:"varint,8,opt,name=time,proto3" json:"time,omitempty"`
PeopleNum int64 `protobuf:"varint,6,opt,name=peopleNum,proto3" json:"peopleNum,omitempty"`
Time int64 `protobuf:"varint,7,opt,name=time,proto3" json:"time,omitempty"`
}
func (x *ElementMessage) Reset() {
*x = ElementMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_message_message_proto_msgTypes[6]
mi := &file_proto_message_message_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -564,7 +192,7 @@ func (x *ElementMessage) String() string {
func (*ElementMessage) ProtoMessage() {}
func (x *ElementMessage) ProtoReflect() protoreflect.Message {
mi := &file_proto_message_message_proto_msgTypes[6]
mi := &file_proto_message_message_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -577,7 +205,7 @@ func (x *ElementMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use ElementMessage.ProtoReflect.Descriptor instead.
func (*ElementMessage) Descriptor() ([]byte, []int) {
return file_proto_message_message_proto_rawDescGZIP(), []int{6}
return file_proto_message_message_proto_rawDescGZIP(), []int{1}
}
func (x *ElementMessage) GetType() ElementMessageType {
@ -615,13 +243,6 @@ func (x *ElementMessage) GetSeek() float64 {
return 0
}
func (x *ElementMessage) GetCurrent() *Current {
if x != nil {
return x.Current
}
return nil
}
func (x *ElementMessage) GetPeopleNum() int64 {
if x != nil {
return x.PeopleNum
@ -641,94 +262,39 @@ var File_proto_message_message_proto protoreflect.FileDescriptor
var file_proto_message_message_proto_rawDesc = []byte{
0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2f,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x0d, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x6f, 0x76,
0x69, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x6c, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x69, 0x76, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x74, 0x6d, 0x70, 0x53, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x74, 0x6d, 0x70,
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x6f, 0x76, 0x69, 0x65, 0x49, 0x6e, 0x66,
0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f,
0x72, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a,
0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65,
0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7d, 0x0a, 0x09, 0x4d, 0x6f, 0x76, 0x69, 0x65, 0x49,
0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x6f,
0x76, 0x69, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1c, 0x0a,
0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63,
0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72,
0x65, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x73,
0x65, 0x65, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x01, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x69,
0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e,
0x67, 0x22, 0x58, 0x0a, 0x07, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x05,
0x6d, 0x6f, 0x76, 0x69, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x6f, 0x76, 0x69, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x6d,
0x6f, 0x76, 0x69, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x0a,
0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65,
0x6e, 0x64, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x6e, 0x64,
0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x08, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x62, 0x69,
0x6c, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x69, 0x6c, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x56, 0x65, 0x6e,
0x64, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08, 0x62, 0x69, 0x6c, 0x69, 0x62,
0x69, 0x6c, 0x69, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x62, 0x69, 0x6c, 0x69, 0x62,
0x69, 0x6c, 0x69, 0x22, 0x88, 0x01, 0x0a, 0x12, 0x42, 0x69, 0x6c, 0x69, 0x62, 0x69, 0x6c, 0x69,
0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x76,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x76, 0x69, 0x64, 0x12, 0x10,
0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x63, 0x69, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x65, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04,
0x65, 0x70, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1e,
0x0a, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x86,
0x02, 0x0a, 0x0e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01,
0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x6b, 0x18, 0x05,
0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x73, 0x65, 0x65, 0x6b, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x75,
0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x6f,
0x70, 0x6c, 0x65, 0x4e, 0x75, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x65,
0x6f, 0x70, 0x6c, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18,
0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2a, 0xdb, 0x01, 0x0a, 0x12, 0x45, 0x6c, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45,
0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x48, 0x41, 0x54, 0x5f, 0x4d,
0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x4c, 0x41, 0x59,
0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x55, 0x53, 0x45, 0x10, 0x04, 0x12, 0x0e, 0x0a,
0x0a, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x5f, 0x53, 0x45, 0x45, 0x4b, 0x10, 0x05, 0x12, 0x0c, 0x0a,
0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x46, 0x41, 0x53, 0x54, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x54,
0x4f, 0x4f, 0x5f, 0x53, 0x4c, 0x4f, 0x57, 0x10, 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x48, 0x41,
0x4e, 0x47, 0x45, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x48,
0x41, 0x4e, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x45, 0x4b, 0x10, 0x09, 0x12, 0x12, 0x0a, 0x0e, 0x43,
0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x10, 0x0a, 0x12,
0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x56, 0x49, 0x45, 0x53,
0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x50, 0x45, 0x4f,
0x50, 0x4c, 0x45, 0x10, 0x0c, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12,
0x0a, 0x04, 0x73, 0x65, 0x65, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x73, 0x65,
0x65, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01,
0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e,
0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67,
0x22, 0xcb, 0x01, 0x0a, 0x0e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79,
0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x01, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x6b,
0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x73, 0x65, 0x65, 0x6b, 0x12, 0x1c, 0x0a, 0x09,
0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x4e, 0x75, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52,
0x09, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69,
0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x2a, 0xdb,
0x01, 0x0a, 0x12, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x10, 0x0a,
0x0c, 0x43, 0x48, 0x41, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12,
0x08, 0x0a, 0x04, 0x50, 0x4c, 0x41, 0x59, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x55,
0x53, 0x45, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x5f, 0x53, 0x45,
0x45, 0x4b, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x46, 0x41, 0x53, 0x54,
0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4c, 0x4f, 0x57, 0x10, 0x07,
0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x10,
0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x45, 0x4b,
0x10, 0x09, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x43, 0x55, 0x52,
0x52, 0x45, 0x4e, 0x54, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45,
0x5f, 0x4d, 0x4f, 0x56, 0x49, 0x45, 0x53, 0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41,
0x4e, 0x47, 0x45, 0x5f, 0x50, 0x45, 0x4f, 0x50, 0x4c, 0x45, 0x10, 0x0c, 0x42, 0x06, 0x5a, 0x04,
0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -744,32 +310,19 @@ func file_proto_message_message_proto_rawDescGZIP() []byte {
}
var file_proto_message_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proto_message_message_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_proto_message_message_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proto_message_message_proto_goTypes = []interface{}{
(ElementMessageType)(0), // 0: proto.ElementMessageType
(*BaseMovieInfo)(nil), // 1: proto.BaseMovieInfo
(*MovieInfo)(nil), // 2: proto.MovieInfo
(*Status)(nil), // 3: proto.Status
(*Current)(nil), // 4: proto.Current
(*VendorInfo)(nil), // 5: proto.VendorInfo
(*BilibiliVendorInfo)(nil), // 6: proto.BilibiliVendorInfo
(*ElementMessage)(nil), // 7: proto.ElementMessage
nil, // 8: proto.BaseMovieInfo.HeadersEntry
(ElementMessageType)(0), // 0: proto.ElementMessageType
(*Status)(nil), // 1: proto.Status
(*ElementMessage)(nil), // 2: proto.ElementMessage
}
var file_proto_message_message_proto_depIdxs = []int32{
8, // 0: proto.BaseMovieInfo.headers:type_name -> proto.BaseMovieInfo.HeadersEntry
5, // 1: proto.BaseMovieInfo.vendorInfo:type_name -> proto.VendorInfo
1, // 2: proto.MovieInfo.base:type_name -> proto.BaseMovieInfo
2, // 3: proto.Current.movie:type_name -> proto.MovieInfo
3, // 4: proto.Current.status:type_name -> proto.Status
6, // 5: proto.VendorInfo.bilibili:type_name -> proto.BilibiliVendorInfo
0, // 6: proto.ElementMessage.type:type_name -> proto.ElementMessageType
4, // 7: proto.ElementMessage.current:type_name -> proto.Current
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
0, // 0: proto.ElementMessage.type:type_name -> proto.ElementMessageType
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_proto_message_message_proto_init() }
@ -779,30 +332,6 @@ func file_proto_message_message_proto_init() {
}
if !protoimpl.UnsafeEnabled {
file_proto_message_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BaseMovieInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_message_message_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MovieInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_message_message_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Status); i {
case 0:
return &v.state
@ -814,43 +343,7 @@ func file_proto_message_message_proto_init() {
return nil
}
}
file_proto_message_message_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Current); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_message_message_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VendorInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_message_message_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BilibiliVendorInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_message_message_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
file_proto_message_message_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ElementMessage); i {
case 0:
return &v.state
@ -863,15 +356,13 @@ func file_proto_message_message_proto_init() {
}
}
}
file_proto_message_message_proto_msgTypes[4].OneofWrappers = []interface{}{}
file_proto_message_message_proto_msgTypes[6].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_message_message_proto_rawDesc,
NumEnums: 1,
NumMessages: 8,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},

@ -19,56 +19,18 @@ enum ElementMessageType {
CHANGE_PEOPLE = 12;
}
message BaseMovieInfo {
string url = 1;
string name = 2;
bool live = 3;
bool proxy = 4;
bool rtmpSource = 5;
string type = 6;
map<string, string> headers = 7;
VendorInfo vendorInfo = 8;
}
message MovieInfo {
string id = 1;
BaseMovieInfo base = 2;
int64 createdAt = 3;
string creator = 4;
}
message Status {
double seek = 1;
double rate = 2;
bool playing = 3;
}
message Current {
MovieInfo movie = 1;
Status status = 2;
}
message VendorInfo {
string vendor = 1;
bool shared = 2;
optional BilibiliVendorInfo bilibili = 3;
}
message BilibiliVendorInfo {
string bvid = 1;
uint64 cid = 2;
uint64 epid = 3;
uint64 quality = 4;
string vendorName = 5;
}
message ElementMessage {
ElementMessageType type = 1;
string sender = 2;
string message = 3;
double rate = 4;
double seek = 5;
optional Current current = 6;
int64 peopleNum = 7;
int64 time = 8;
int64 peopleNum = 6;
int64 time = 7;
}

@ -193,10 +193,8 @@ func PushMovie(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -233,10 +231,8 @@ func PushMovies(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -313,10 +309,8 @@ func EditMovie(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -346,10 +340,8 @@ func DelMovie(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -372,10 +364,8 @@ func ClearMovies(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -400,10 +390,8 @@ func SwapMovie(ctx *gin.Context) {
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
},
Type: pb.ElementMessageType_CHANGE_MOVIES,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -436,56 +424,13 @@ func ChangeCurrentMovie(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
}
current, err := genCurrent(ctx, room.Current(), user.ID)
if err != nil {
if err := room.Broadcast(&op.ElementMessage{
Type: pb.ElementMessageType_CHANGE_CURRENT,
Sender: user.Username,
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
current.UpdateSeek()
if (current.Movie.Movie.Base.VendorInfo.Vendor == "") || (current.Movie.Movie.Base.VendorInfo.Vendor != "" && current.Movie.Movie.Base.VendorInfo.Shared) {
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_CURRENT,
Sender: user.Username,
Current: current.Proto(),
},
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
} else {
if err := room.SendToUser(user, &op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_CURRENT,
Sender: user.Username,
Current: current.Proto(),
},
}); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
m := &pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_CURRENT,
Sender: user.Username,
}
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: m,
BeforeSendFunc: func(sendTo *op.User) error {
current, err := genCurrent(ctx, room.Current(), sendTo.ID)
if err != nil {
return err
}
current.UpdateSeek()
m.Current = current.Proto()
return nil
},
}, op.WithIgnoreId(user.ID)); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
}
ctx.Status(http.StatusNoContent)
}

@ -141,7 +141,7 @@ func genRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) []*model.RoomListResp
resp[i] = &model.RoomListResp{
RoomId: r.ID,
RoomName: r.Name,
PeopleNum: op.ClientNum(r.ID),
PeopleNum: op.PeopleNum(r.ID),
NeedPassword: len(r.HashedPassword) != 0,
CreatorID: r.CreatorID,
Creator: op.GetUserName(r.CreatorID),
@ -160,7 +160,7 @@ func CheckRoom(ctx *gin.Context) {
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"peopleNum": op.ClientNum(r.ID),
"peopleNum": op.PeopleNum(r.ID),
"needPassword": r.NeedPassword(),
"creator": op.GetUserName(r.CreatorID),
}))

@ -7,7 +7,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
json "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/op"
pb "github.com/synctv-org/synctv/proto/message"
@ -34,7 +33,7 @@ func NewWebSocketHandler(wss *utils.WebSocket) gin.HandlerFunc {
func NewWSMessageHandler(u *op.User, r *op.Room) func(c *websocket.Conn) error {
return func(c *websocket.Conn) error {
client, err := r.RegClient(u, c)
client, err := r.NewClient(u, c)
if err != nil {
log.Errorf("ws: register client error: %v", err)
wc, err2 := c.NextWriter(websocket.BinaryMessage)
@ -43,16 +42,14 @@ func NewWSMessageHandler(u *op.User, r *op.Room) func(c *websocket.Conn) error {
}
defer wc.Close()
em := op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
},
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
}
return em.Encode(wc)
}
log.Infof("ws: room %s user %s connected", r.Name, u.Username)
defer func() {
r.UnregisterClient(u)
r.UnregisterClient(client)
client.Close()
log.Infof("ws: room %s user %s disconnected", r.Name, u.Username)
}()
@ -69,11 +66,6 @@ func handleWriterMessage(c *op.Client) error {
return err
}
if err = v.BeforeSend(c.User()); err != nil {
log.Debugf("ws: room %s user %s before send message error: %v", c.Room().Name, c.User().Username, err)
continue
}
if err = v.Encode(wc); err != nil {
log.Debugf("ws: room %s user %s encode message error: %v", c.Room().Name, c.User().Username, err)
return err
@ -88,7 +80,6 @@ func handleWriterMessage(c *op.Client) error {
func handleReaderMessage(c *op.Client) error {
defer c.Close()
var msg pb.ElementMessage
for {
t, rd, err := c.NextReader()
if err != nil {
@ -105,78 +96,49 @@ func handleReaderMessage(c *op.Client) error {
if data, err = io.ReadAll(rd); err != nil {
log.Errorf("ws: room %s user %s read message error: %v", c.Room().Name, c.User().Username, err)
if err := c.Send(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
},
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
}); err != nil {
log.Errorf("ws: room %s user %s send error message error: %v", c.Room().Name, c.User().Username, err)
return err
}
continue
}
var msg pb.ElementMessage
if err := proto.Unmarshal(data, &msg); err != nil {
log.Errorf("ws: room %s user %s decode message error: %v", c.Room().Name, c.User().Username, err)
if err := c.Send(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
},
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
}); err != nil {
log.Errorf("ws: room %s user %s send error message error: %v", c.Room().Name, c.User().Username, err)
return err
}
continue
}
case websocket.TextMessage:
if err := json.NewDecoder(rd).Decode(&msg); err != nil {
log.Errorf("ws: room %s user %s decode message error: %v", c.Room().Name, c.User().Username, err)
if err := c.Send(&op.ElementMessage{
ElementMessage: &pb.ElementMessage{
Type: pb.ElementMessageType_ERROR,
Message: err.Error(),
},
}); err != nil {
log.Errorf("ws: room %s user %s send error message error: %v", c.Room().Name, c.User().Username, err)
return err
}
continue
log.Debugf("ws: receive room %s user %s message: %+v", c.Room().Name, c.User().Username, msg.String())
if err = handleElementMsg(c, &msg); err != nil {
log.Errorf("ws: room %s user %s handle message error: %v", c.Room().Name, c.User().Username, err)
return err
}
default:
log.Errorf("ws: room %s user %s receive unknown message type: %d", c.Room().Name, c.User().Username, t)
continue
}
log.Debugf("ws: receive room %s user %s message: %+v", c.Room().Name, c.User().Username, msg.String())
switch t {
case websocket.BinaryMessage:
err = handleElementMsg(c.Room(), &msg, func(em *pb.ElementMessage) error {
em.Sender = c.User().Username
return c.Send(&op.ElementMessage{ElementMessage: em})
}, func(em *pb.ElementMessage, bc ...op.BroadcastConf) error {
em.Sender = c.User().Username
return c.Broadcast(&op.ElementMessage{ElementMessage: em}, bc...)
})
case websocket.TextMessage:
err = handleElementMsg(c.Room(), &msg, func(em *pb.ElementMessage) error {
em.Sender = c.User().Username
return c.Send(&op.ElementJsonMessage{ElementMessage: em})
}, func(em *pb.ElementMessage, bc ...op.BroadcastConf) error {
em.Sender = c.User().Username
return c.Broadcast(&op.ElementJsonMessage{ElementMessage: em}, bc...)
})
}
if err != nil {
log.Errorf("ws: room %s user %s handle message error: %v", c.Room().Name, c.User().Username, err)
return err
}
}
}
type send func(*pb.ElementMessage) error
type broadcast func(*pb.ElementMessage, ...op.BroadcastConf) error
func handleElementMsg(r *op.Room, msg *pb.ElementMessage, send send, broadcast broadcast) error {
func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error {
var send = func(em *pb.ElementMessage) error {
em.Sender = cli.User().Username
return cli.Send((*op.ElementMessage)(em))
}
var broadcast = func(em *pb.ElementMessage, bc ...op.BroadcastConf) error {
em.Sender = cli.User().Username
return cli.Broadcast((*op.ElementMessage)(em), bc...)
}
var timeDiff float64
if msg.Time != 0 {
timeDiff = time.Since(time.UnixMilli(msg.Time)).Seconds()
@ -200,37 +162,37 @@ func handleElementMsg(r *op.Room, msg *pb.ElementMessage, send send, broadcast b
broadcast(&pb.ElementMessage{
Type: pb.ElementMessageType_CHAT_MESSAGE,
Message: msg.Message,
}, op.WithSendToSelf())
})
case pb.ElementMessageType_PLAY:
status := r.SetStatus(true, msg.Seek, msg.Rate, timeDiff)
status := cli.Room().SetStatus(true, msg.Seek, msg.Rate, timeDiff)
broadcast(&pb.ElementMessage{
Type: pb.ElementMessageType_PLAY,
Seek: status.Seek,
Rate: status.Rate,
})
}, op.WithIgnoreClient(cli))
case pb.ElementMessageType_PAUSE:
status := r.SetStatus(false, msg.Seek, msg.Rate, timeDiff)
status := cli.Room().SetStatus(false, msg.Seek, msg.Rate, timeDiff)
broadcast(&pb.ElementMessage{
Type: pb.ElementMessageType_PAUSE,
Seek: status.Seek,
Rate: status.Rate,
})
}, op.WithIgnoreClient(cli))
case pb.ElementMessageType_CHANGE_RATE:
status := r.SetSeekRate(msg.Seek, msg.Rate, timeDiff)
status := cli.Room().SetSeekRate(msg.Seek, msg.Rate, timeDiff)
broadcast(&pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_RATE,
Seek: status.Seek,
Rate: status.Rate,
})
}, op.WithIgnoreClient(cli))
case pb.ElementMessageType_CHANGE_SEEK:
status := r.SetSeekRate(msg.Seek, msg.Rate, timeDiff)
status := cli.Room().SetSeekRate(msg.Seek, msg.Rate, timeDiff)
broadcast(&pb.ElementMessage{
Type: pb.ElementMessageType_CHANGE_SEEK,
Seek: status.Seek,
Rate: status.Rate,
})
}, op.WithIgnoreClient(cli))
case pb.ElementMessageType_CHECK_SEEK:
status := r.Current().Status
status := cli.Room().Current().Status
if status.Seek+maxInterval < msg.Seek+timeDiff {
send(&pb.ElementMessage{
Type: pb.ElementMessageType_TOO_FAST,

Loading…
Cancel
Save