X-Git-Url: https://code.octet-stream.net/broadcaster/blobdiff_plain/7423c6c97eb5d6dc063e7185c50137bbb5e25a23..7b615b3c71825b5b229b78509a16db37e1d3f38d:/server/web_sync.go diff --git a/server/web_sync.go b/server/web_sync.go new file mode 100644 index 0000000..a6b1a6e --- /dev/null +++ b/server/web_sync.go @@ -0,0 +1,162 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "sort" + "strconv" + "strings" + + "code.octet-stream.net/broadcaster/internal/protocol" + "golang.org/x/net/websocket" +) + +func WebSync(ws *websocket.Conn) { + log.Println("A web user connected with WebSocket") + buf := make([]byte, 16384) + + badRead := false + isAuthenticated := false + var user User + for { + // Ignore any massively oversize messages + n, err := ws.Read(buf) + if err != nil { + if user.Username != "" { + log.Println("Lost websocket to user:", user) + } else { + log.Println("Lost unauthenticated website websocket") + } + return + } + if n == len(buf) { + badRead = true + continue + } else if badRead { + badRead = false + continue + } + + if !isAuthenticated { + token := string(buf[:n]) + u, err := users.GetUserForSession(token) + if err != nil { + log.Println("Could not find user for offered token", token, err) + ws.Close() + return + } + user = u + log.Println("User authenticated:", user) + isAuthenticated = true + + go KeepWebUpdated(ws) + + // send initial playlists message + err = sendRadioStatusToWeb(ws) + if err != nil { + return + } + } + } +} + +type WebStatusData struct { + Radios []WebRadioStatus +} + +type WebRadioStatus struct { + Name string + LocalTime string + TimeZone string + ChannelClass string + ChannelState string + Playlist string + File string + Status string + Id string + DisableCancel bool + FilesInSync bool +} + +func sendRadioStatusToWeb(ws *websocket.Conn) error { + webStatuses := make([]WebRadioStatus, 0) + radioStatuses := status.Statuses() + keys := make([]int, 0) + for i := range radioStatuses { + keys = append(keys, i) + } + sort.Ints(keys) + for _, i := range keys { + v := radioStatuses[i] + radio, err := db.GetRadio(i) + if err != nil { + continue + } + var channelClass, channelState string + if v.PTT { + channelClass = "ptt" + channelState = "PTT" + } else if v.COS { + channelClass = "cos" + channelState = "RX" + } else { + channelClass = "clear" + channelState = "CLEAR" + } + var statusText string + var disableCancel bool + if v.Status == protocol.StatusIdle { + statusText = "Idle" + disableCancel = true + } else if v.Status == protocol.StatusDelay { + statusText = fmt.Sprintf("Performing delay before transmit: %ds remain", v.DelaySecondsRemaining) + disableCancel = false + } else if v.Status == protocol.StatusChannelInUse { + statusText = fmt.Sprintf("Waiting for channel to clear: %ds", v.WaitingForChannelSeconds) + disableCancel = false + } else if v.Status == protocol.StatusPlaying { + statusText = fmt.Sprintf("Playing: %d:%02d", v.PlaybackSecondsElapsed/60, v.PlaybackSecondsElapsed%60) + disableCancel = false + } + playlist := v.Playlist + if playlist == "" { + playlist = "-" + } + filename := v.Filename + if filename == "" { + filename = "-" + } + webStatuses = append(webStatuses, WebRadioStatus{ + Name: radio.Name, + LocalTime: v.LocalTime, + TimeZone: v.TimeZone, + ChannelClass: channelClass, + ChannelState: channelState, + Playlist: playlist, + File: filename, + Status: statusText, + Id: strconv.Itoa(i), + DisableCancel: disableCancel, + FilesInSync: v.FilesInSync, + }) + } + data := WebStatusData{ + Radios: webStatuses, + } + buf := new(strings.Builder) + tmpl := template.Must(template.ParseFS(content, "templates/radios.partial.html")) + tmpl.Execute(buf, data) + _, err := ws.Write([]byte(buf.String())) + return err +} + +func KeepWebUpdated(ws *websocket.Conn) { + for { + <-status.ChangeChannel() + err := sendRadioStatusToWeb(ws) + if err != nil { + return + } + } +}