Zippytal-Node/squadVideoChannelHandler.go

399 lines
12 KiB
Go

package localserver
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/pion/webrtc/v3"
)
type SquadVideoChannelsHandler struct {
SquadId string
SquadName string
HostId string
SquadMembersId []string
DataChannels map[string]*DataChannel
VideoChanDataChannels map[string]*DataChannel
VideoChanDataChannelsFlag *uint32
DataChannelsFlag *uint32
VideoChannelsFlag *uint32
VideoChannel *VideoChannel
reqChans []chan<- *SquadRequest
}
func NewSquadVideoChannelsHandler(hostId string, squadId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (squadVideoChannelsHandler *SquadVideoChannelsHandler, err error) {
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
if err != nil {
if os.IsNotExist(err) {
logger.Printf("creating videoChannels directory for squad %s...\n", squadId)
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"), 0700)
if mkdirErr != nil {
return nil, mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "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
}
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
if err != nil {
return nil, err
}
} else {
return
}
}
var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "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))
videoChannelFlag := uint32(0)
videoChanDCFlag := uint32(0)
squadVideoChannelsHandler = &SquadVideoChannelsHandler{
SquadId: squadId,
HostId: hostId,
SquadMembersId: authorizedMembers,
DataChannels: dataChannels,
VideoChanDataChannels: make(map[string]*DataChannel),
VideoChanDataChannelsFlag: &videoChanDCFlag,
DataChannelsFlag: dataChannelFlag,
VideoChannel: vc,
VideoChannelsFlag: &videoChannelFlag,
}
return
}
func (zvch *SquadVideoChannelsHandler) 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(&SquadResponse{
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 *SquadVideoChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
return
}
func (zvch *SquadVideoChannelsHandler) Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error) {
reqChan, done, errCh = make(chan *SquadRequest), 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.handleSquadRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
func (zvch *SquadVideoChannelsHandler) 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 *SquadVideoChannelsHandler) JoinVideoChannel(channelId string, userId string, sdp string) (err error) {
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
videoChannel := zvch.VideoChannel
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)
}
}
}
signalMembers(zvch.SquadMembersId)
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 *SquadVideoChannelsHandler) LeaveVideoChannel(channelId string, userId string) (err error) {
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
videoChannel := zvch.VideoChannel
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)
}
}
}
signalMembers(zvch.SquadMembersId)
return
})
return
}
func (zvch *SquadVideoChannelsHandler) 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.OnOpen(func() {
fmt.Println("datachann in squad video chann fking created")
bs, err := json.Marshal(map[string]any{
"type": "init_video_channel",
"from": NodeID,
"to": command[1],
"payload": map[string]any{
"videoChannel": zvch.VideoChannel,
},
})
if err != nil {
fmt.Println(err)
}
_ = dc.DataChannel.SendText(string(bs))
})
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 *SquadVideoChannelsHandler) handleSquadRequest(ctx context.Context, req *SquadRequest) (err error) {
switch req.ReqType {
case LEAVE_ZONE:
logger.Println("*-----------------handling leaving squad---------------*")
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
vc := zvch.VideoChannel
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_SQUAD_AUTHORIZED_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var index int
var found bool
for i, m := range zvch.SquadMembersId {
if m == req.Payload["userId"].(string) {
index = i
found = true
break
}
}
if !found {
err = fmt.Errorf("no such member in squad")
return
}
zvch.SquadMembersId = append(zvch.SquadMembersId[:index], zvch.SquadMembersId[index+1:]...)
case string(NEW_AUTHORIZED_SQUAD_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var contain bool
for _, m := range zvch.SquadMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
}
if !contain {
zvch.SquadMembersId = append(zvch.SquadMembersId, req.Payload["userId"].(string))
}
case JOIN_VIDEO_CHANNEL:
fmt.Println("wuwuuwuwwuuwuw i got this")
fmt.Println(req.Payload)
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 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) {
err = zvch.VideoChannel.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) {
err = zvch.VideoChannel.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) {
err = zvch.VideoChannel.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) {
fmt.Println("uwuwuw adding candidate",req.Payload["candidate"].(string))
err = zvch.VideoChannel.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
}