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_AUDIO_CHANNEL = "join_audio_channel"
|
|
LEAVE_AUDIO_CHANNEL = "leave_audio_channel"
|
|
LIST_AUDIO_CHANNELS = "list_audio_channels"
|
|
GET_AUDIO_CHANNELS = "get_audio_channels"
|
|
GET_AUDIO_CHANNEL = "get_audio_channel"
|
|
CREATE_AUDIO_CHANNEL = "create_audio_channel"
|
|
DELETE_AUDIO_CHANNEL = "delete_audio_channel"
|
|
EDIT_AUDIO_CHANNEL_TYPE = "edit_audio_channel_type"
|
|
EDIT_AUDIO_CHANNEL_NAME = "edit_audio_channel_name"
|
|
ADD_AUDIO_CHANNEL_MEMBERS = "add_audio_channel_members"
|
|
REMOVE_AUDIO_CHANNEL_MEMBER = "remove_audio_channel_member"
|
|
)
|
|
|
|
const (
|
|
USER_JOINED_AUDIO_CHANNEL = "user_joined_audio_channel"
|
|
USER_LEFT_AUDIO_CHANNEL = "user_left_audio_channel"
|
|
AUDIO_CHANNNEL_NAME_EDITED = "audio_channel_name_edited"
|
|
AUDIO_CHANNNEL_TYPE_EDITED = "audio_channel_type_edited"
|
|
AUDIO_CHANNEL_MEMBER_REMOVED = "audio_channel_member_removed"
|
|
AUDIO_CHANNEL_MEMBER_ADDED = "audio_channel_member_added"
|
|
ADDED_IN_AUDIO_CHANNEL = "added_in_audio_channel"
|
|
REMOVED_FROM_AUDIO_CHANNEL = "removed_from_audio_channel"
|
|
)
|
|
|
|
type AudioChannelMember struct {
|
|
}
|
|
|
|
type AudioChannelConfig struct {
|
|
CurrentMembersId []string
|
|
ID string `json:"id"`
|
|
Owner string `json:"owner"`
|
|
ChannelType string `json:"channelType"`
|
|
Members []string `json:"members"`
|
|
}
|
|
|
|
type ZoneAudioChannelsHandler struct {
|
|
HostId string
|
|
ZoneId string
|
|
ZoneMembersId []string
|
|
DataChannels map[string]*DataChannel
|
|
DataChannelsFlag *uint32
|
|
AudioChannelsFlag *uint32
|
|
AudioChannels map[string]*AudioChannel
|
|
reqChans []chan<- *ZoneRequest
|
|
}
|
|
|
|
const LOBBY string = "lobby"
|
|
|
|
func NewZoneAudioChannelsHandler(hostId string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (zoneAudioChannelsHandler *ZoneAudioChannelsHandler, err error) {
|
|
var dirs []fs.DirEntry
|
|
dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "audioChannels"))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
logger.Printf("creating audioChannels directory for zone %s...\n", zoneId)
|
|
mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zoneId, "audioChannels", LOBBY), 0700)
|
|
if mkdirErr != nil {
|
|
return nil, mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "audioChannels", LOBBY, "audioChannelConfig.json"))
|
|
if ferr != nil {
|
|
return nil, ferr
|
|
}
|
|
baseConfig := AudioChannelConfig{
|
|
ID: LOBBY,
|
|
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, "audioChannels"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
audioChannels := make(map[string]*AudioChannel)
|
|
for _, audioChannel := range dirs {
|
|
var bs []byte
|
|
bs, err = os.ReadFile(filepath.Join("data", "zones", zoneId, "audioChannels", audioChannel.Name(), "audioChannelConfig.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Println(string(bs))
|
|
var acc AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &acc); err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Println("audioChannels data :", acc.ID, acc.ChannelType, acc.Owner, acc.Members)
|
|
ac := NewAudioChannel(acc.ID, acc.Owner, acc.ChannelType, acc.Members, make([]string, 0), make(map[string]*AudioChannelMember))
|
|
audioChannels[acc.ID] = ac
|
|
}
|
|
audioChannelFlag := uint32(0)
|
|
zoneAudioChannelsHandler = &ZoneAudioChannelsHandler{
|
|
HostId: hostId,
|
|
ZoneId: zoneId,
|
|
ZoneMembersId: authorizedMembers,
|
|
DataChannels: dataChannels,
|
|
DataChannelsFlag: dataChannelFlag,
|
|
AudioChannels: audioChannels,
|
|
AudioChannelsFlag: &audioChannelFlag,
|
|
}
|
|
return
|
|
}
|
|
|
|
type SendDCMessageFunc = func(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error)
|
|
|
|
func (zach *ZoneAudioChannelsHandler) sendZoneRequest(reqType string, from string, payload map[string]interface{}) {
|
|
go func() {
|
|
for _, rc := range zach.reqChans {
|
|
rc <- &ZoneRequest{
|
|
ReqType: reqType,
|
|
From: from,
|
|
Payload: payload,
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) 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(zach.DataChannelsFlag, func() (err error) {
|
|
if _, ok := zach.DataChannels[to]; ok {
|
|
bs, jsonErr := json.Marshal(&ZoneResponse{
|
|
Type: reqType,
|
|
From: zach.HostId,
|
|
To: to,
|
|
Payload: payload,
|
|
})
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
err = zach.DataChannels[to].DataChannel.SendText(string(bs))
|
|
}
|
|
return
|
|
}); err != nil {
|
|
errCh <- err
|
|
return
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
return done, errCh
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
|
|
for _, member := range authorizedMembers {
|
|
if serr := zach.SetAllPublicAudioChannelForUser(member); serr != nil {
|
|
logger.Println(serr)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) 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)
|
|
zach.reqChans = append(zach.reqChans, reqChan)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
done <- struct{}{}
|
|
return
|
|
case req := <-publisher:
|
|
if err := zach.handleZoneRequest(ctx, req); err != nil {
|
|
errCh <- err
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
|
|
d, e := zach.sendDataChannelMessage(string(AUDIO_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 (zach *ZoneAudioChannelsHandler) GetAudioChannels(userId string, channelsId ...interface{}) (err error) {
|
|
audioChannels := make([]*AudioChannel, 0, len(channelsId))
|
|
for _, id := range channelsId {
|
|
if _, ok := id.(string); !ok {
|
|
err = fmt.Errorf("id of wrong type")
|
|
return
|
|
}
|
|
logger.Println("audioChannel from get audioChannels", id.(string))
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[id.(string)]; ok {
|
|
logger.Println(zach.AudioChannels[id.(string)])
|
|
audioChannels = append(audioChannels, zach.AudioChannels[id.(string)])
|
|
}
|
|
return
|
|
})
|
|
}
|
|
answer := &ZoneResponse{
|
|
Type: "get_audio_channels_response",
|
|
From: "",
|
|
To: "",
|
|
Payload: map[string]interface{}{
|
|
"audioChannels": audioChannels,
|
|
},
|
|
}
|
|
bs, jsonErr := json.Marshal(answer)
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
err = atomicallyExecute(zach.DataChannelsFlag, func() (err error) {
|
|
if _, ok := zach.DataChannels[userId]; ok {
|
|
err = zach.DataChannels[userId].DataChannel.SendText(string(bs))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) ListAudioChannels() (audioChannels []*AudioChannel, err error) {
|
|
audioChannels = make([]*AudioChannel, 0)
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
for _, audioChannel := range zach.AudioChannels {
|
|
audioChannels = append(audioChannels, audioChannel)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) AddNewAudioChannel(channelName string, owner string, channelType string, members []interface{}) (err error) {
|
|
if _, ok := zach.AudioChannels[channelName]; ok {
|
|
err = fmt.Errorf("an audio channel with this name already exist")
|
|
return
|
|
}
|
|
mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelName), 0700)
|
|
if mkdirErr != nil {
|
|
return mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelName, "audioChannelConfig.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 := &AudioChannelConfig{
|
|
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 acc AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &acc); err != nil {
|
|
return err
|
|
}
|
|
ac := NewAudioChannel(acc.ID, acc.Owner, acc.ChannelType, acc.Members, acc.CurrentMembersId, make(map[string]*AudioChannelMember))
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
zach.AudioChannels[ac.ID] = ac
|
|
return
|
|
})
|
|
newAudioChannelForMembers := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelName,
|
|
})
|
|
done, e := zach.sendDataChannelMessage("get_audio_channels_response", "node", member, map[string]interface{}{
|
|
"audioChannels": []*AudioChannel{zach.AudioChannels[channelName]},
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch ac.ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
err = newAudioChannelForMembers(zach.ZoneMembersId)
|
|
return
|
|
})
|
|
case PRIVATE:
|
|
err = newAudioChannelForMembers(ac.Members)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) DeleteAudioChannel(channelId string) (err error) {
|
|
if err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
defer delete(zach.AudioChannels, channelId)
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no corresponding audio channel")
|
|
return
|
|
}
|
|
removeKnownAudioChannels := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": member,
|
|
})
|
|
select {
|
|
case <-done:
|
|
continue
|
|
case err = <-e:
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch zach.AudioChannels[channelId].ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = removeKnownAudioChannels(zach.ZoneMembersId)
|
|
case PRIVATE:
|
|
err = removeKnownAudioChannels(zach.AudioChannels[channelId].Members)
|
|
}
|
|
return
|
|
}); err != nil {
|
|
return
|
|
}
|
|
if err = os.RemoveAll(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId)); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) EditAudioChannelName(channelId string, newAudioChannelId string) (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding audioChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var audioChannelConfig AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &audioChannelConfig); err != nil {
|
|
return
|
|
}
|
|
audioChannelConfig.ID = newAudioChannelId
|
|
bs, err = json.Marshal(&audioChannelConfig)
|
|
if err = os.Rename(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId), filepath.Join("data", "zones", zach.ZoneId, "audioChannels", newAudioChannelId)); err != nil {
|
|
return
|
|
}
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", newAudioChannelId, "audioChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
|
defer func() {
|
|
if e := f.Close(); e != nil {
|
|
logger.Println(e)
|
|
}
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = f.Write(bs); err != nil {
|
|
return err
|
|
}
|
|
audioChannel := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember))
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
defer delete(zach.AudioChannels, channelId)
|
|
zach.AudioChannels[newAudioChannelId] = audioChannel
|
|
updateKnownAudioChannels := func(members []string) (err error) {
|
|
for _, member := range members {
|
|
zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": newAudioChannelId,
|
|
})
|
|
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zach.sendDataChannelMessage(AUDIO_CHANNNEL_NAME_EDITED, "node", member, map[string]interface{}{
|
|
"formerAudioChannelId": channelId,
|
|
"newAudioChannelId": newAudioChannelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case channelErr := <-e:
|
|
logger.Println(channelErr)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
switch audioChannel.ChannelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
err = updateKnownAudioChannels(zach.ZoneMembersId)
|
|
case PRIVATE:
|
|
err = updateKnownAudioChannels(audioChannel.Members)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) EditAudioChannelType(channelId string, channelType string) (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding audioChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var audioChannelConfig AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &audioChannelConfig); err != nil {
|
|
return
|
|
}
|
|
audioChannelConfig.ChannelType = channelType
|
|
bs, err = json.Marshal(&audioChannelConfig)
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.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
|
|
}
|
|
audioChannel := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember))
|
|
switch channelType {
|
|
case BROADCAST:
|
|
fallthrough
|
|
case PUBLIC:
|
|
var members = []string{}
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
members = append(members, zach.ZoneMembersId...)
|
|
return
|
|
})
|
|
for _, member := range zach.ZoneMembersId {
|
|
if pubErr := zach.SetAudioChannelPublicForUser(channelId, member); pubErr != nil {
|
|
logger.Println(pubErr)
|
|
}
|
|
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"channelType": channelType,
|
|
})
|
|
}
|
|
case PRIVATE:
|
|
var members = []string{}
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
members = append(members, zach.ZoneMembersId...)
|
|
return
|
|
})
|
|
for _, member := range zach.ZoneMembersId {
|
|
if pubErr := zach.SetAudioChannelPrivateForUser(channelId, member); pubErr != nil {
|
|
logger.Println(pubErr)
|
|
}
|
|
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"channelType": channelType,
|
|
})
|
|
}
|
|
}
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
zach.AudioChannels[channelId] = audioChannel
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) AddAudioChannelsMembers(channelId string, members []interface{}) (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding audioChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var audioChannelConfig AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &audioChannelConfig); err != nil {
|
|
return
|
|
}
|
|
logger.Printf("%s - %s - %s -%v\n", audioChannelConfig.ID, audioChannelConfig.ChannelType, audioChannelConfig.Owner, members)
|
|
addedMembers := make([]string, 0)
|
|
memberLoop:
|
|
for _, audioChannelMember := range members {
|
|
logger.Println("entering broadcast loop")
|
|
if _, ok := audioChannelMember.(string); !ok {
|
|
continue
|
|
}
|
|
for _, member := range audioChannelConfig.Members {
|
|
if member == audioChannelMember {
|
|
continue memberLoop
|
|
}
|
|
}
|
|
audioChannelConfig.Members = append(audioChannelConfig.Members, audioChannelMember.(string))
|
|
addedMembers = append(addedMembers, audioChannelMember.(string))
|
|
logger.Println("sending zone request", ADD_KNOWN_AUDIO_CHANNEL)
|
|
zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": audioChannelMember,
|
|
"channelId": channelId,
|
|
})
|
|
logger.Println("--------------done")
|
|
}
|
|
bs, err = json.Marshal(&audioChannelConfig)
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.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
|
|
}
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
zach.AudioChannels[channelId].Members = audioChannelConfig.Members
|
|
ac := zach.AudioChannels[channelId]
|
|
broadcastLoop:
|
|
for _, member := range ac.Members {
|
|
for _, m := range addedMembers {
|
|
if member == m {
|
|
done, e := zach.sendDataChannelMessage(ADDED_IN_AUDIO_CHANNEL, "node", member, map[string]interface{}{
|
|
"audioChannel": ac,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
continue broadcastLoop
|
|
}
|
|
if _, ok := zach.DataChannels[member]; ok {
|
|
done, e := zach.sendDataChannelMessage(AUDIO_CHANNEL_MEMBER_ADDED, "node", member, map[string]interface{}{
|
|
"userId": m,
|
|
"channelId": ac.ID,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) RemoveAudioChannelMember(channelId string, channelMember string) (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no coresponding audioChannel")
|
|
return
|
|
}
|
|
bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
var audioChannelConfig AudioChannelConfig
|
|
if err = json.Unmarshal(bs, &audioChannelConfig); err != nil {
|
|
logger.Println(string(bs))
|
|
logger.Println("json error right here")
|
|
return
|
|
}
|
|
if channelMember == audioChannelConfig.Owner {
|
|
err = fmt.Errorf("you cannot remove the owner from the audioChannel")
|
|
return
|
|
}
|
|
var index int
|
|
var contain bool
|
|
for i, member := range audioChannelConfig.Members {
|
|
if member == channelMember {
|
|
index = i
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
err = fmt.Errorf("member %s not in the channel %s", channelMember, channelId)
|
|
return
|
|
}
|
|
audioChannelConfig.Members = append(audioChannelConfig.Members[:index], audioChannelConfig.Members[index+1:]...)
|
|
bs, err = json.Marshal(&audioChannelConfig)
|
|
if err != nil {
|
|
logger.Println("json error there")
|
|
return
|
|
}
|
|
f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.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
|
|
}
|
|
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
zach.AudioChannels[channelId].Members = audioChannelConfig.Members
|
|
ac := zach.AudioChannels[channelId]
|
|
broadcastLoop:
|
|
for _, member := range ac.Members {
|
|
if member == channelMember {
|
|
continue broadcastLoop
|
|
}
|
|
done, e := zach.sendDataChannelMessage(AUDIO_CHANNEL_MEMBER_REMOVED, "node", member, map[string]interface{}{
|
|
"userId": channelMember,
|
|
"channelId": ac.ID,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
return
|
|
})
|
|
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": channelMember,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", channelMember, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": channelMember,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
logger.Println(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) SetAudioChannelPrivateForUser(channelId string, member string) (err error) {
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if audioChannel, ok := zach.AudioChannels[channelId]; ok {
|
|
var contain bool
|
|
for _, m := range audioChannel.Members {
|
|
if m == member {
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", member, map[string]interface{}{
|
|
"channelId": channelId,
|
|
"userId": member,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
}
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) SetAudioChannelPublicForUser(channelId string, member string) (err error) {
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if audioChannel, ok := zach.AudioChannels[channelId]; ok {
|
|
var contain bool
|
|
for _, m := range audioChannel.Members {
|
|
if m == member {
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
|
|
"userId": member,
|
|
"channelId": channelId,
|
|
})
|
|
done, e := zach.sendDataChannelMessage(ADDED_IN_AUDIO_CHANNEL, "node", member, map[string]interface{}{
|
|
"audioChannel": audioChannel,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err = <-e:
|
|
}
|
|
}
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) SetAllPublicAudioChannelForUser(userId string) (err error) {
|
|
audioChannels, err := zach.ListAudioChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, audioChannel := range audioChannels {
|
|
if audioChannel.ChannelType == PUBLIC || audioChannel.ChannelType == BROADCAST {
|
|
if audioChannel.ID == LOBBY {
|
|
if err = zach.AddAudioChannelsMembers(audioChannel.ID, []interface{}{userId}); err != nil {
|
|
logger.Println(err)
|
|
}
|
|
continue
|
|
}
|
|
if err = zach.SetAudioChannelPublicForUser(audioChannel.ID, userId); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) RemoveUserFromAllAudioChannels(userId string) (err error) {
|
|
audioChannels, err := zach.ListAudioChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, audioChannel := range audioChannels {
|
|
if err = zach.RemoveAudioChannelMember(audioChannel.ID, userId); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) RemoveAllUserAudioChannels(userId string) (err error) {
|
|
audioChannels, err := zach.ListAudioChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, audioChannel := range audioChannels {
|
|
if audioChannel.Owner == userId {
|
|
if derr := zach.DeleteAudioChannel(audioChannel.ID); derr != nil {
|
|
logger.Println("**************", derr)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) JoinAudioChannel(channelId string, userId string, sdp string) (err error) {
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no audio channel with corresponding id")
|
|
return
|
|
}
|
|
audioChannel := zach.AudioChannels[channelId]
|
|
audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId, userId)
|
|
audioChannel.CurrentMembers[userId] = &AudioChannelMember{}
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zach.sendDataChannelMessage(USER_JOINED_AUDIO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err := <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
if audioChannel.ChannelType == PRIVATE {
|
|
signalMembers(audioChannel.Members)
|
|
} else {
|
|
signalMembers(zach.ZoneMembersId)
|
|
}
|
|
d, e := audioChannel.HandleOffer(context.Background(), channelId, userId, sdp, zach.HostId, zach.sendDataChannelMessage, zach.signalCandidate)
|
|
select {
|
|
case <-d:
|
|
case err = <-e:
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) LeaveAudioChannel(channelId string, userId string) (err error) {
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[channelId]; !ok {
|
|
err = fmt.Errorf("no audio channel with corresponding id")
|
|
return
|
|
}
|
|
audioChannel := zach.AudioChannels[channelId]
|
|
var index int
|
|
var contain bool
|
|
for i, v := range audioChannel.CurrentMembersId {
|
|
if v == userId {
|
|
index = i
|
|
contain = true
|
|
}
|
|
}
|
|
if !contain {
|
|
err = fmt.Errorf("this channel does not contain the provided user Id")
|
|
return
|
|
}
|
|
defer audioChannel.HandleLeavingMember(userId)
|
|
if len(audioChannel.CurrentMembersId) <= 1 {
|
|
audioChannel.CurrentMembersId = make([]string, 0)
|
|
} else {
|
|
audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId[:index], audioChannel.CurrentMembersId[index+1:]...)
|
|
}
|
|
delete(audioChannel.CurrentMembers, userId)
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zach.sendDataChannelMessage(USER_LEFT_AUDIO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case err := <-e:
|
|
logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
if audioChannel.ChannelType == PRIVATE {
|
|
signalMembers(audioChannel.Members)
|
|
} else {
|
|
signalMembers(zach.ZoneMembersId)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zach *ZoneAudioChannelsHandler) 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 _, ac := range zach.AudioChannels {
|
|
var contain bool
|
|
var id string
|
|
for _, member := range ac.CurrentMembersId {
|
|
if member == req.Payload["userId"].(string) {
|
|
id = member
|
|
contain = true
|
|
logger.Printf("*------------------id is %s--------------*\n", id)
|
|
break
|
|
}
|
|
}
|
|
if contain {
|
|
err = zach.LeaveAudioChannel(ac.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 zach.ZoneMembersId {
|
|
if m == req.Payload["userId"].(string) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
zach.ZoneMembersId = append(zach.ZoneMembersId[:index], zach.ZoneMembersId[index+1:]...)
|
|
if err = zach.RemoveAllUserAudioChannels(req.Payload["userId"].(string)); err != nil {
|
|
logger.Println("****______________", err)
|
|
}
|
|
err = zach.RemoveUserFromAllAudioChannels(req.Payload["userId"].(string))
|
|
case string(NEW_AUTHORIZED_ZONE_MEMBER):
|
|
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
zach.ZoneMembersId = append(zach.ZoneMembersId, req.Payload["userId"].(string))
|
|
err = zach.SetAllPublicAudioChannelForUser(req.Payload["userId"].(string))
|
|
case JOIN_AUDIO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = zach.JoinAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
case LEAVE_AUDIO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = zach.LeaveAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string))
|
|
case ADD_AUDIO_CHANNEL_MEMBERS:
|
|
if err = verifyFieldsString(req.Payload, "channelId"); err != nil {
|
|
return
|
|
}
|
|
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
|
|
return
|
|
}
|
|
err = zach.AddAudioChannelsMembers(req.Payload["channelId"].(string), req.Payload["members"].([]interface{}))
|
|
case REMOVE_AUDIO_CHANNEL_MEMBER:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = zach.RemoveAudioChannelMember(req.Payload["channelId"].(string), req.Payload["userId"].(string))
|
|
if err != nil {
|
|
logger.Println("an error occured in add known audioChannel", err)
|
|
return
|
|
}
|
|
case EDIT_AUDIO_CHANNEL_NAME:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "newAudioChannelId"); err != nil {
|
|
return
|
|
}
|
|
if err = zach.EditAudioChannelName(req.Payload["channelId"].(string), req.Payload["newAudioChannelId"].(string)); err != nil {
|
|
return
|
|
}
|
|
case EDIT_AUDIO_CHANNEL_TYPE:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "channelType"); err != nil {
|
|
return
|
|
}
|
|
if err = zach.EditAudioChannelType(req.Payload["channelId"].(string), req.Payload["channelType"].(string)); err != nil {
|
|
return
|
|
}
|
|
case DELETE_AUDIO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId"); err != nil {
|
|
return
|
|
}
|
|
err = zach.DeleteAudioChannel(req.Payload["channelId"].(string))
|
|
case CREATE_AUDIO_CHANNEL:
|
|
if err = verifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil {
|
|
return
|
|
}
|
|
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
|
|
return
|
|
}
|
|
err = zach.AddNewAudioChannel(req.Payload["channelId"].(string), req.Payload["owner"].(string), req.Payload["channelType"].(string), req.Payload["members"].([]interface{}))
|
|
case GET_AUDIO_CHANNELS:
|
|
if err = verifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil {
|
|
return
|
|
}
|
|
err = zach.GetAudioChannels(req.From, req.Payload["channelsId"].([]interface{})...)
|
|
case string(AUDIO_CHANNEL_WEBRTC_COUNTER_OFFER):
|
|
logger.Println("handling audio channel counter offer")
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleCounterOffer(ctx, req.Payload["userId"].(string), zach.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER):
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zach.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER):
|
|
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
return
|
|
})
|
|
case string(AUDIO_CHANNEL_WEBRTC_CANDIDATE):
|
|
logger.Println("handling audio 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(zach.AudioChannelsFlag, func() (err error) {
|
|
if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok {
|
|
err = fmt.Errorf("no channel corresponging the one requested")
|
|
return
|
|
}
|
|
err = zach.AudioChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{
|
|
Candidate: req.Payload["candidate"].(string),
|
|
SDPMid: &sdpMid,
|
|
SDPMLineIndex: &SDPMLineIndex,
|
|
}, req.Payload["userId"].(string))
|
|
return
|
|
})
|
|
return
|
|
default:
|
|
logger.Println("audio channel handler still in process of implementation")
|
|
}
|
|
return
|
|
}
|