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 } } }