Zippytal-Node/zoneVideoChannelsHandler.go

1072 lines
34 KiB
Go

package localserver
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"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
ZoneName string
HostId string
ZoneMembersId []string
DataChannels map[string]*DataChannel
VideoChanDataChannels map[string]*DataChannel
VideoChanDataChannelsFlag *uint32
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(dataPath, "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(dataPath, "data", "zones", zoneId, "videoChannels", MEETING), 0700)
if mkdirErr != nil {
return nil, mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "zones", zoneId, "videoChannels", MEETING, "videoChannelConfig.json"))
defer func() {
_ = file.Close()
}()
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
}
dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "videoChannels"))
if err != nil {
return nil, err
}
} else {
return
}
}
videoChannels := make(map[string]*VideoChannel)
for _, videoChannel := range dirs {
if strings.HasPrefix(videoChannel.Name(), ".") {
continue
}
var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "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)
videoChanDCFlag := uint32(0)
zoneVideoChannelsHandler = &ZoneVideoChannelsHandler{
ZoneId: zoneId,
HostId: hostId,
ZoneMembersId: authorizedMembers,
DataChannels: dataChannels,
VideoChanDataChannels: make(map[string]*DataChannel),
VideoChanDataChannelsFlag: &videoChanDCFlag,
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.VideoChanDataChannelsFlag, func() (err error) {
if _, ok := zvch.VideoChanDataChannels[to]; ok {
bs, jsonErr := json.Marshal(&ZoneResponse{
Type: reqType,
From: from,
To: to,
Payload: payload,
})
if jsonErr != nil {
return jsonErr
}
err = zvch.VideoChanDataChannels[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
})
}
done, e := zvch.sendDataChannelMessage("get_video_channels_response", "node", userId, map[string]interface{}{
"videoChannels": videoChannels,
})
select {
case <-done:
fmt.Println("done")
fmt.Println(videoChannels)
case err = <-e:
}
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 channelName == "" {
err = fmt.Errorf("not a valid channel name provided")
return
}
if _, ok := zvch.VideoChannels[channelName]; ok {
err = fmt.Errorf("an video channel with this name already exist")
return
}
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "zones", zvch.ZoneId, "videoChannels", channelName), 0700)
if mkdirErr != nil {
return mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "zones", zvch.ZoneId, "videoChannels", channelName, "videoChannelConfig.json"))
defer func() {
_ = file.Close()
}()
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
}
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(dataPath, "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(dataPath, "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(dataPath, "data", "zones", zvch.ZoneId, "videoChannels", channelId), filepath.Join(dataPath, "data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId)); err != nil {
return
}
f, err := os.OpenFile(filepath.Join(dataPath, "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(dataPath, "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(dataPath, "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.AddVideoChannelsMembers(channelId, []any{member}); pubErr != nil {
logger.Println(pubErr)
}
zvch.sendDataChannelMessage(VIDEO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
}
case PRIVATE:
for _, member := range zvch.ZoneMembersId {
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
}
membersStr, err := ToStringSlice(members)
if err != nil {
return
}
bs, err := os.ReadFile(filepath.Join(dataPath, "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 membersStr {
logger.Println("entering broadcast loop")
for _, member := range videoChannelConfig.Members {
if member == videoChannelMember {
continue memberLoop
}
}
videoChannelConfig.Members = append(videoChannelConfig.Members, videoChannelMember)
addedMembers = append(addedMembers, videoChannelMember)
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(dataPath, "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
})
for _, member := range vc.Members {
for _, m := range addedMembers {
if member == m {
if m != videoChannelConfig.Owner {
go func() {
bs, err = json.Marshal(map[string]any{
"zoneId": zvch.ZoneId,
"channelId": channelId,
"zoneHost": NodeID,
})
if err != nil {
return
}
zvch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "added_in_video_channel",
"title": "Added in video channel 🎥",
"body": fmt.Sprintf("Added in channel %s in zone %s", channelId, zvch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{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)
}
} else 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(dataPath, "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(dataPath, "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,
})
if channelMember != videoChannelConfig.Owner {
bs, err = json.Marshal(map[string]any{
"zoneId": zvch.ZoneId,
"channelId": channelId,
"zoneHost": NodeID,
})
if err != nil {
return err
}
zvch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "removed_from_video_channel",
"title": "Removed from video channel ⛔️",
"body": fmt.Sprintf("You have no longer access to the channel %s in zone %s", channelId, zvch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{channelMember},
})
}
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) 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 err = zvch.AddVideoChannelsMembers(videoChannel.ID, []interface{}{userId}); err != nil {
logger.Println(err)
}
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) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
var label string
_ = atomicallyExecute(dc.l, func() (err error) {
label = dc.DataChannel.Label()
return
})
if strings.Contains(label, "video_channel_data") {
command := strings.Split(label, "|")
catched = true
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
zvch.VideoChanDataChannels[command[1]] = dc
return
})
dc.DataChannel.OnClose(func() {
fmt.Println("closing gratefully video channel dc...")
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
delete(zvch.VideoChanDataChannels, command[1])
fmt.Println("dc closed gracefully...")
return
})
})
dc.DataChannel.OnError(func(err error) {
fmt.Println("error in video channel dc...")
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
delete(zvch.VideoChanDataChannels, command[1])
fmt.Println("dc closed on error...")
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
var found bool
for i, m := range zvch.ZoneMembersId {
if m == req.Payload["userId"].(string) {
index = i
found = true
break
}
}
if !found {
err = fmt.Errorf("no such member in zone")
return
}
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
}
var contain bool
for _, m := range zvch.ZoneMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
}
if !contain {
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
}