1053 lines
33 KiB
Go
1053 lines
33 KiB
Go
package localserver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
const (
|
|
JOIN_VIDEO_CHANNEL = "join_video_channel"
|
|
LEAVE_VIDEO_CHANNEL = "leave_video_channel"
|
|
LIST_VIDEO_CHANNELS = "list_video_channels"
|
|
GET_VIDEO_CHANNELS = "get_video_channels"
|
|
GET_VIDEO_CHANNEL = "get_video_channel"
|
|
CREATE_VIDEO_CHANNEL = "create_video_channel"
|
|
DELETE_VIDEO_CHANNEL = "delete_video_channel"
|
|
EDIT_VIDEO_CHANNEL_TYPE = "edit_video_channel_type"
|
|
EDIT_VIDEO_CHANNEL_NAME = "edit_video_channel_name"
|
|
ADD_VIDEO_CHANNEL_MEMBERS = "add_video_channel_members"
|
|
REMOVE_VIDEO_CHANNEL_MEMBER = "remove_video_channel_member"
|
|
)
|
|
|
|
const (
|
|
USER_JOINED_VIDEO_CHANNEL = "user_joined_video_channel"
|
|
USER_LEFT_VIDEO_CHANNEL = "user_left_video_channel"
|
|
VIDEO_CHANNNEL_NAME_EDITED = "video_channel_name_edited"
|
|
VIDEO_CHANNNEL_TYPE_EDITED = "video_channel_type_edited"
|
|
VIDEO_CHANNEL_MEMBER_REMOVED = "video_channel_member_removed"
|
|
VIDEO_CHANNEL_MEMBER_ADDED = "video_channel_member_added"
|
|
ADDED_IN_VIDEO_CHANNEL = "added_in_video_channel"
|
|
REMOVED_FROM_VIDEO_CHANNEL = "removed_from_video_channel"
|
|
)
|
|
|
|
type VideoChannelMember struct {
|
|
}
|
|
|
|
type VideoChannelConfig struct {
|
|
ID string `json:"id"`
|
|
Owner string `json:"owner"`
|
|
ChannelType string `json:"channelType"`
|
|
CurrentMembersId []string
|
|
Members []string `json:"members"`
|
|
}
|
|
|
|
type ZoneVideoChannelsHandler struct {
|
|
ZoneId string
|
|
HostId string
|
|
ZoneMembersId []string
|
|
DataChannels map[string]*DataChannel
|
|
DataChannelsFlag *uint32
|
|
VideoChannelsFlag *uint32
|
|
VideoChannels map[string]*VideoChannel
|
|
reqChans []chan<- *ZoneRequest
|
|
}
|
|
|
|
const MEETING string = "meeting"
|
|
|
|
func NewZoneVideoChannelsHandler(hostId string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (zoneVideoChannelsHandler *ZoneVideoChannelsHandler, err error) {
|
|
var dirs []fs.DirEntry
|
|
dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "videoChannels"))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
logger.Printf("creating videoChannels directory for zone %s...\n", zoneId)
|
|
mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zoneId, "videoChannels", MEETING), 0700)
|
|
if mkdirErr != nil {
|
|
return nil, mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "videoChannels", MEETING, "videoChannelConfig.json"))
|
|
if ferr != nil {
|
|
return nil, ferr
|
|
}
|
|
baseConfig := VideoChannelConfig{
|
|
ID: MEETING,
|
|
Owner: owner,
|
|
ChannelType: "public",
|
|
CurrentMembersId: make([]string, 0),
|
|
Members: make([]string, 0),
|
|
}
|
|
bs, jsonErr := json.Marshal(baseConfig)
|
|
if jsonErr != nil {
|
|
return nil, jsonErr
|
|
}
|
|
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
|
|
return nil, writeErr
|
|
}
|
|
_ = file.Close()
|
|
dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "videoChannels"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
videoChannels := make(map[string]*VideoChannel)
|
|
for _, videoChannel := range dirs {
|
|
var bs []byte
|
|
bs, err = os.ReadFile(filepath.Join("data", "zones", zoneId, "videoChannels", videoChannel.Name(), "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Println(string(bs))
|
|
var vcc VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &vcc); err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Println("videoChannels data :", vcc.ID, vcc.ChannelType, vcc.Owner, vcc.Members)
|
|
vc := NewVideoChannel(vcc.ID, vcc.Owner, vcc.ChannelType, vcc.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
videoChannels[vc.ID] = vc
|
|
}
|
|
videoChannelFlag := uint32(0)
|
|
zoneVideoChannelsHandler = &ZoneVideoChannelsHandler{
|
|
ZoneId: zoneId,
|
|
HostId: hostId,
|
|
ZoneMembersId: authorizedMembers,
|
|
DataChannels: dataChannels,
|
|
DataChannelsFlag: dataChannelFlag,
|
|
VideoChannels: videoChannels,
|
|
VideoChannelsFlag: &videoChannelFlag,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) sendZoneRequest(reqType string, from string, payload map[string]interface{}) {
|
|
go func() {
|
|
for _, rc := range zvch.reqChans {
|
|
rc <- &ZoneRequest{
|
|
ReqType: reqType,
|
|
From: from,
|
|
Payload: payload,
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) {
|
|
done, errCh := make(chan struct{}), make(chan error)
|
|
go func() {
|
|
if err := atomicallyExecute(zvch.DataChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.DataChannels[to]; ok {
|
|
bs, jsonErr := json.Marshal(&ZoneResponse{
|
|
Type: reqType,
|
|
From: from,
|
|
To: to,
|
|
Payload: payload,
|
|
})
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
err = zvch.DataChannels[to].DataChannel.SendText(string(bs))
|
|
}
|
|
return
|
|
}); err != nil {
|
|
errCh <- err
|
|
return
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
return done, errCh
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
|
|
for _, member := range authorizedMembers {
|
|
if serr := zvch.SetAllPublicVideoChannelForUser(member); serr != nil {
|
|
logger.Println(serr)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) {
|
|
reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error)
|
|
zvch.reqChans = append(zvch.reqChans, reqChan)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
done <- struct{}{}
|
|
return
|
|
case req := <-publisher:
|
|
if err := zvch.handleZoneRequest(ctx, req); err != nil {
|
|
errCh <- err
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
|
|
d, e := zvch.sendDataChannelMessage(string(VIDEO_CHANNEL_WEBRTC_CANDIDATE), from, to, map[string]interface{}{
|
|
"from": from,
|
|
"to": to,
|
|
"candidate": candidate.ToJSON().Candidate,
|
|
"sdpMid": *candidate.ToJSON().SDPMid,
|
|
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
|
})
|
|
select {
|
|
case <-d:
|
|
case err = <-e:
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) GetVideoChannels(userId string, channelsId ...interface{}) (err error) {
|
|
videoChannels := make([]*VideoChannel, 0, len(channelsId))
|
|
for _, id := range channelsId {
|
|
if _, ok := id.(string); !ok {
|
|
err = fmt.Errorf("id of wrong type")
|
|
return
|
|
}
|
|
logger.Println("videoChannel from get videoChannels", id.(string))
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[id.(string)]; ok {
|
|
logger.Println(zvch.VideoChannels[id.(string)])
|
|
videoChannels = append(videoChannels, zvch.VideoChannels[id.(string)])
|
|
}
|
|
return
|
|
})
|
|
}
|
|
answer := &ZoneResponse{
|
|
Type: "get_video_channels_response",
|
|
From: "",
|
|
To: "",
|
|
Payload: map[string]interface{}{
|
|
"videoChannels": videoChannels,
|
|
},
|
|
}
|
|
bs, jsonErr := json.Marshal(answer)
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
_ = atomicallyExecute(zvch.DataChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.DataChannels[userId]; ok {
|
|
err = zvch.DataChannels[userId].DataChannel.SendText(string(bs))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) ListVideoChannels() (videoChannels []*VideoChannel, err error) {
|
|
videoChannels = make([]*VideoChannel, 0)
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
for _, videoChannel := range zvch.VideoChannels {
|
|
videoChannels = append(videoChannels, videoChannel)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) AddNewVideoChannel(channelName string, owner string, channelType string, members []interface{}) (err error) {
|
|
if _, ok := zvch.VideoChannels[channelName]; ok {
|
|
err = fmt.Errorf("an video channel with this name already exist")
|
|
return
|
|
}
|
|
mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelName), 0700)
|
|
if mkdirErr != nil {
|
|
return mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelName, "videoChannelConfig.json"))
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
m := make([]string, 0, len(members))
|
|
for _, member := range members {
|
|
if mbr, ok := member.(string); ok && mbr != owner {
|
|
m = append(m, mbr)
|
|
}
|
|
}
|
|
baseConfig := &VideoChannelConfig{
|
|
ID: channelName,
|
|
Owner: owner,
|
|
ChannelType: channelType,
|
|
Members: append(m, owner),
|
|
}
|
|
bs, jsonErr := json.Marshal(baseConfig)
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
|
|
return writeErr
|
|
}
|
|
err = file.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var vcc VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &vcc); err != nil {
|
|
return err
|
|
}
|
|
vc := NewVideoChannel(vcc.ID, vcc.Owner, vcc.ChannelType, vcc.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
vc.CurrentMembers = make(map[string]*VideoChannelMember)
|
|
zvch.VideoChannels[vc.ID] = vc
|
|
return
|
|
})
|
|
newVideoChannelForMembers := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zvch.sendZoneRequest(ADD_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelName,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage("get_video_channels_response", "node", member, map[string]interface{}{
|
|
"videoChannels": []*VideoChannel{zvch.VideoChannels[channelName]},
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch vc.ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
err = newVideoChannelForMembers(zvch.ZoneMembersId)
|
|
return
|
|
})
|
|
case PRIVATE:
|
|
err = newVideoChannelForMembers(vc.Members)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) DeleteVideoChannel(channelId string) (err error) {
|
|
if err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
defer delete(zvch.VideoChannels, channelId)
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no corresponding video channel")
|
|
return
|
|
}
|
|
removeKnownVideoChannels := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zvch.sendZoneRequest(REMOVE_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": member,
|
|
})
|
|
select {
|
|
case <-done:
|
|
continue
|
|
case err = <-e:
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch zvch.VideoChannels[channelId].ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = removeKnownVideoChannels(zvch.ZoneMembersId)
|
|
case PRIVATE:
|
|
err = removeKnownVideoChannels(zvch.VideoChannels[channelId].Members)
|
|
}
|
|
return
|
|
}); err != nil {
|
|
return
|
|
}
|
|
if err = os.RemoveAll(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId)); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) EditVideoChannelName(channelId string, newVideoChannelId string) (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding videoChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var videoChannelConfig VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &videoChannelConfig); err != nil {
|
|
return
|
|
}
|
|
videoChannelConfig.ID = newVideoChannelId
|
|
bs, err = json.Marshal(&videoChannelConfig)
|
|
if err = os.Rename(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId), filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId)); err != nil {
|
|
return
|
|
}
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId, "videoChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = f.Write(bs); err != nil {
|
|
return err
|
|
}
|
|
videoChannel := NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
defer delete(zvch.VideoChannels, channelId)
|
|
zvch.VideoChannels[newVideoChannelId] = videoChannel
|
|
updateKnownVideoChannels := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zvch.sendZoneRequest(ADD_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": newVideoChannelId,
|
|
})
|
|
zvch.sendZoneRequest(REMOVE_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage(VIDEO_CHANNNEL_NAME_EDITED, "node", member, map[string]interface{}{
|
|
"formerVideoChannelId": channelId,
|
|
"newVideoChannelId": newVideoChannelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case channelErr := <-e:
|
|
logger.Println(channelErr)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch videoChannel.ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = updateKnownVideoChannels(zvch.ZoneMembersId)
|
|
case PRIVATE:
|
|
err = updateKnownVideoChannels(videoChannel.Members)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) EditVideoChannelType(channelId string, channelType string) (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding videoChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var videoChannelConfig VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &videoChannelConfig); err != nil {
|
|
return
|
|
}
|
|
videoChannelConfig.ChannelType = channelType
|
|
bs, err = json.Marshal(&videoChannelConfig)
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = f.Write(bs); err != nil {
|
|
return err
|
|
}
|
|
videoChannel := NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
switch channelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
var members = []string{}
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
members = append(members, zvch.ZoneMembersId...)
|
|
return
|
|
})
|
|
for _, member := range zvch.ZoneMembersId {
|
|
if pubErr := zvch.SetVideoChannelPublicForUser(channelId, member); pubErr != nil {
|
|
logger.Println(pubErr)
|
|
}
|
|
zvch.sendDataChannelMessage(VIDEO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"channelType": channelType,
|
|
})
|
|
}
|
|
case PRIVATE:
|
|
var members = []string{}
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
members = append(members, zvch.ZoneMembersId...)
|
|
return
|
|
})
|
|
for _, member := range zvch.ZoneMembersId {
|
|
if pubErr := zvch.SetVideoChannelPrivateForUser(channelId, member); pubErr != nil {
|
|
logger.Println(pubErr)
|
|
}
|
|
zvch.sendDataChannelMessage(VIDEO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"channelType": channelType,
|
|
})
|
|
}
|
|
}
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
zvch.VideoChannels[channelId] = videoChannel
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) AddVideoChannelsMembers(channelId string, members []interface{}) (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding videoChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var videoChannelConfig VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &videoChannelConfig); err != nil {
|
|
return
|
|
}
|
|
logger.Printf("%s - %s - %s -%v\n", videoChannelConfig.ID, videoChannelConfig.ChannelType, videoChannelConfig.Owner, members)
|
|
addedMembers := make([]string, 0)
|
|
memberLoop:
|
|
for _, videoChannelMember := range members {
|
|
logger.Println("entering broadcast loop")
|
|
if _, ok := videoChannelMember.(string); !ok {
|
|
continue
|
|
}
|
|
for _, member := range videoChannelConfig.Members {
|
|
if member == videoChannelMember {
|
|
continue memberLoop
|
|
}
|
|
}
|
|
videoChannelConfig.Members = append(videoChannelConfig.Members, videoChannelMember.(string))
|
|
addedMembers = append(addedMembers, videoChannelMember.(string))
|
|
logger.Println("sending zone request", ADD_KNOWN_VIDEO_CHANNEL)
|
|
zvch.sendZoneRequest(ADD_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": videoChannelMember,
|
|
"channelId": channelId,
|
|
})
|
|
logger.Println("--------------done")
|
|
}
|
|
bs, err = json.Marshal(&videoChannelConfig)
|
|
if err != nil {
|
|
return
|
|
}
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = f.Write(bs); err != nil {
|
|
return err
|
|
}
|
|
vc := NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
zvch.VideoChannels[channelId] = vc
|
|
return
|
|
})
|
|
broadcastLoop:
|
|
for _, member := range vc.Members {
|
|
for _, m := range addedMembers {
|
|
if member == m {
|
|
done, e := zvch.sendDataChannelMessage(ADDED_IN_VIDEO_CHANNEL, "node", member, map[string]interface{}{
|
|
"videoChannel": vc,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
continue broadcastLoop
|
|
}
|
|
if _, ok := zvch.DataChannels[member]; ok {
|
|
done, e := zvch.sendDataChannelMessage(VIDEO_CHANNEL_MEMBER_ADDED, "node", member, map[string]interface{}{
|
|
"userId": m,
|
|
"channelId": vc.ID,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) RemoveVideoChannelMember(channelId string, channelMember string) (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding videoChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var videoChannelConfig VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &videoChannelConfig); err != nil {
|
|
logger.Println(string(bs))
|
|
logger.Println("json error right here")
|
|
return
|
|
}
|
|
if channelMember == videoChannelConfig.Owner {
|
|
err = fmt.Errorf("you cannot remove the owner from the videoChannel")
|
|
return
|
|
}
|
|
var index int
|
|
var contain bool
|
|
for i, member := range videoChannelConfig.Members {
|
|
if member == channelMember {
|
|
index = i
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
err = fmt.Errorf("member %s not in the channel %s", channelMember, channelId)
|
|
return
|
|
}
|
|
videoChannelConfig.Members = append(videoChannelConfig.Members[:index], videoChannelConfig.Members[index+1:]...)
|
|
bs, err = json.Marshal(&videoChannelConfig)
|
|
if err != nil {
|
|
logger.Println("json error there")
|
|
return
|
|
}
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
if err != nil {
|
|
return
|
|
}
|
|
logger.Println(string(bs))
|
|
if _, err = f.Write(bs); err != nil {
|
|
return
|
|
}
|
|
var vc *VideoChannel
|
|
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
vc = NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
zvch.VideoChannels[channelId] = vc
|
|
return
|
|
})
|
|
zvch.sendZoneRequest(REMOVE_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": channelMember,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", channelMember, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": channelMember,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
broadcastLoop:
|
|
for _, member := range vc.Members {
|
|
if member == channelMember {
|
|
continue broadcastLoop
|
|
}
|
|
done, e := zvch.sendDataChannelMessage(VIDEO_CHANNEL_MEMBER_REMOVED, "node", member, map[string]interface{}{
|
|
"userId": channelMember,
|
|
"channelId": vc.ID,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) SetVideoChannelPrivateForUser(channelId string, member string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if videoChannel, ok := zvch.VideoChannels[channelId]; ok {
|
|
var contain bool
|
|
for _, m := range videoChannel.Members {
|
|
if m == member {
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
zvch.sendZoneRequest(REMOVE_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": member,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
}
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) SetVideoChannelPublicForUser(channelId string, member string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if videoChannel, ok := zvch.VideoChannels[channelId]; ok {
|
|
var contain bool
|
|
for _, m := range videoChannel.Members {
|
|
if m == member {
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
zvch.sendZoneRequest(ADD_KNOWN_VIDEO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zvch.sendDataChannelMessage(ADDED_IN_VIDEO_CHANNEL, "node", member, map[string]interface{}{
|
|
"videoChannel": videoChannel,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
}
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) SetAllPublicVideoChannelForUser(userId string) (err error) {
|
|
videoChannels, err := zvch.ListVideoChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, videoChannel := range videoChannels {
|
|
if videoChannel.ChannelType == PUBLIC || videoChannel.ChannelType == BROADCAST {
|
|
if videoChannel.ID == MEETING {
|
|
if err = zvch.AddVideoChannelsMembers(videoChannel.ID, []interface{}{userId}); err != nil {
|
|
logger.Println(err)
|
|
}
|
|
continue
|
|
}
|
|
if err = zvch.SetVideoChannelPublicForUser(videoChannel.ID, userId); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) RemoveUserFromAllVideoChannels(userId string) (err error) {
|
|
videoChannels, err := zvch.ListVideoChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, videoChannel := range videoChannels {
|
|
if err = zvch.RemoveVideoChannelMember(videoChannel.ID, userId); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) RemoveAllUserVideoChannels(userId string) (err error) {
|
|
videoChannels, err := zvch.ListVideoChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, videoChannel := range videoChannels {
|
|
if videoChannel.Owner == userId {
|
|
if derr := zvch.DeleteVideoChannel(videoChannel.ID); derr != nil {
|
|
logger.Println("**************", derr)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) JoinVideoChannel(channelId string, userId string, sdp string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no video channel with corresponding id")
|
|
return
|
|
}
|
|
videoChannel := zvch.VideoChannels[channelId]
|
|
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId, userId)
|
|
videoChannel.CurrentMembers[userId] = &VideoChannelMember{}
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zvch.sendDataChannelMessage(USER_JOINED_VIDEO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err := <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
if videoChannel.ChannelType == PRIVATE {
|
|
signalMembers(videoChannel.Members)
|
|
} else {
|
|
signalMembers(zvch.ZoneMembersId)
|
|
}
|
|
d, e := videoChannel.HandleOffer(context.Background(), channelId, userId, sdp, zvch.HostId, zvch.sendDataChannelMessage, zvch.signalCandidate)
|
|
select {
|
|
case <-d:
|
|
case err = <-e:
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) LeaveVideoChannel(channelId string, userId string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no video channel with corresponding id")
|
|
return
|
|
}
|
|
videoChannel := zvch.VideoChannels[channelId]
|
|
var index int
|
|
var contain bool
|
|
for i, v := range videoChannel.CurrentMembersId {
|
|
if v == userId {
|
|
index = i
|
|
contain = true
|
|
}
|
|
}
|
|
if !contain {
|
|
err = fmt.Errorf("this channel does not contain the provided user Id")
|
|
return
|
|
}
|
|
defer videoChannel.HandleLeavingMember(userId)
|
|
if len(videoChannel.CurrentMembersId) <= 1 {
|
|
videoChannel.CurrentMembersId = make([]string, 0)
|
|
} else {
|
|
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId[:index], videoChannel.CurrentMembersId[index+1:]...)
|
|
}
|
|
delete(videoChannel.CurrentMembers, userId)
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zvch.sendDataChannelMessage(USER_LEFT_VIDEO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err := <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
if videoChannel.ChannelType == PRIVATE {
|
|
signalMembers(videoChannel.Members)
|
|
} else {
|
|
signalMembers(zvch.ZoneMembersId)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) {
|
|
switch req.ReqType {
|
|
case LEAVE_ZONE:
|
|
logger.Println("*-----------------handling leaving zone---------------*")
|
|
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
for _, vc := range zvch.VideoChannels {
|
|
var contain bool
|
|
var id string
|
|
for _, member := range vc.CurrentMembersId {
|
|
if member == req.Payload["userId"].(string) {
|
|
id = member
|
|
contain = true
|
|
logger.Printf("*------------------id is %s--------------*\n", id)
|
|
break
|
|
}
|
|
}
|
|
if contain {
|
|
err = zvch.LeaveVideoChannel(vc.ID, id)
|
|
break
|
|
}
|
|
}
|
|
case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
|
|
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
var index int
|
|
for i, m := range zvch.ZoneMembersId {
|
|
if m == req.Payload["userId"].(string) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
zvch.ZoneMembersId = append(zvch.ZoneMembersId[:index], zvch.ZoneMembersId[index+1:]...)
|
|
if err = zvch.RemoveAllUserVideoChannels(req.Payload["userId"].(string)); err != nil {
|
|
logger.Println("****______________", err)
|
|
}
|
|
err = zvch.RemoveUserFromAllVideoChannels(req.Payload["userId"].(string))
|
|
case string(NEW_AUTHORIZED_ZONE_MEMBER):
|
|
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
zvch.ZoneMembersId = append(zvch.ZoneMembersId, req.Payload["userId"].(string))
|
|
err = zvch.SetAllPublicVideoChannelForUser(req.Payload["userId"].(string))
|
|
case JOIN_VIDEO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.JoinVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
case LEAVE_VIDEO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.LeaveVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string))
|
|
case ADD_VIDEO_CHANNEL_MEMBERS:
|
|
if err = verifyFieldsString(req.Payload, "channelId"); err != nil {
|
|
return
|
|
}
|
|
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.AddVideoChannelsMembers(req.Payload["channelId"].(string), req.Payload["members"].([]interface{}))
|
|
case REMOVE_VIDEO_CHANNEL_MEMBER:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.RemoveVideoChannelMember(req.Payload["channelId"].(string), req.Payload["userId"].(string))
|
|
if err != nil {
|
|
logger.Println("an error occured in add known videoChannel", err)
|
|
return
|
|
}
|
|
case EDIT_VIDEO_CHANNEL_NAME:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "newVideoChannelId"); err != nil {
|
|
return
|
|
}
|
|
if err = zvch.EditVideoChannelName(req.Payload["channelId"].(string), req.Payload["newVideoChannelId"].(string)); err != nil {
|
|
return
|
|
}
|
|
case EDIT_VIDEO_CHANNEL_TYPE:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "channelType"); err != nil {
|
|
return
|
|
}
|
|
if err = zvch.EditVideoChannelType(req.Payload["channelId"].(string), req.Payload["channelType"].(string)); err != nil {
|
|
return
|
|
}
|
|
case DELETE_VIDEO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.DeleteVideoChannel(req.Payload["channelId"].(string))
|
|
case CREATE_VIDEO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil {
|
|
return
|
|
}
|
|
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.AddNewVideoChannel(req.Payload["channelId"].(string), req.Payload["owner"].(string), req.Payload["channelType"].(string), req.Payload["members"].([]interface{}))
|
|
case GET_VIDEO_CHANNELS:
|
|
if err = verifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.GetVideoChannels(req.From, req.Payload["channelsId"].([]interface{})...)
|
|
case string(VIDEO_CHANNEL_WEBRTC_COUNTER_OFFER):
|
|
logger.Println("handling video channel counter offer")
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zvch.VideoChannels[req.Payload["channelId"].(string)].HandleCounterOffer(ctx, req.Payload["userId"].(string), zvch.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER):
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zvch.VideoChannels[req.Payload["channelId"].(string)].HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zvch.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER):
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zvch.VideoChannels[req.Payload["channelId"].(string)].HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_CANDIDATE):
|
|
logger.Println("handling video channel webrtc candidate")
|
|
logger.Println(req.Payload)
|
|
if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
logger.Println(req.Payload)
|
|
i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
|
|
if convErr != nil {
|
|
return convErr
|
|
}
|
|
SDPMLineIndex := uint16(i)
|
|
sdpMid := req.Payload["sdpMid"].(string)
|
|
logger.Println(sdpMid, SDPMLineIndex)
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zvch.VideoChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{
|
|
Candidate: req.Payload["candidate"].(string),
|
|
SDPMid: &sdpMid,
|
|
SDPMLineIndex: &SDPMLineIndex,
|
|
}, req.Payload["userId"].(string))
|
|
return
|
|
})
|
|
default:
|
|
logger.Println("video channel handler still in process of implementation")
|
|
}
|
|
return
|
|
}
|