]> code.octet-stream.net Git - broadcaster/blob - broadcaster-server/web_sync.go
Add a menu system
[broadcaster] / broadcaster-server / web_sync.go
1 package main
2
3 import (
4 "fmt"
5 "html/template"
6 "log"
7 "sort"
8 "strconv"
9 "strings"
10
11 "code.octet-stream.net/broadcaster/internal/protocol"
12 "golang.org/x/net/websocket"
13 )
14
15 func WebSync(ws *websocket.Conn) {
16 log.Println("A web user connected with WebSocket")
17 buf := make([]byte, 16384)
18
19 badRead := false
20 isAuthenticated := false
21 var user User
22 for {
23 // Ignore any massively oversize messages
24 n, err := ws.Read(buf)
25 if err != nil {
26 if user.Username != "" {
27 log.Println("Lost websocket to user:", user)
28 } else {
29 log.Println("Lost unauthenticated website websocket")
30 }
31 return
32 }
33 if n == len(buf) {
34 badRead = true
35 continue
36 } else if badRead {
37 badRead = false
38 continue
39 }
40
41 if !isAuthenticated {
42 token := string(buf[:n])
43 u, err := users.GetUserForSession(token)
44 if err != nil {
45 log.Println("Could not find user for offered token", token, err)
46 ws.Close()
47 return
48 }
49 user = u
50 log.Println("User authenticated:", user)
51 isAuthenticated = true
52
53 go KeepWebUpdated(ws)
54
55 // send initial playlists message
56 err = sendRadioStatusToWeb(ws)
57 if err != nil {
58 return
59 }
60 }
61 }
62 }
63
64 type WebStatusData struct {
65 Radios []WebRadioStatus
66 }
67
68 type WebRadioStatus struct {
69 Name string
70 LocalTime string
71 TimeZone string
72 ChannelClass string
73 ChannelState string
74 Playlist string
75 File string
76 Status string
77 Id string
78 DisableCancel bool
79 FilesInSync bool
80 }
81
82 func sendRadioStatusToWeb(ws *websocket.Conn) error {
83 webStatuses := make([]WebRadioStatus, 0)
84 radioStatuses := status.Statuses()
85 keys := make([]int, 0)
86 for i := range radioStatuses {
87 keys = append(keys, i)
88 }
89 sort.Ints(keys)
90 for _, i := range keys {
91 v := radioStatuses[i]
92 radio, err := db.GetRadio(i)
93 if err != nil {
94 continue
95 }
96 var channelClass, channelState string
97 if v.PTT {
98 channelClass = "ptt"
99 channelState = "PTT"
100 } else if v.COS {
101 channelClass = "cos"
102 channelState = "RX"
103 } else {
104 channelClass = "clear"
105 channelState = "CLEAR"
106 }
107 var statusText string
108 var disableCancel bool
109 if v.Status == protocol.StatusIdle {
110 statusText = "Idle"
111 disableCancel = true
112 } else if v.Status == protocol.StatusDelay {
113 statusText = fmt.Sprintf("Performing delay before transmit: %ds remain", v.DelaySecondsRemaining)
114 disableCancel = false
115 } else if v.Status == protocol.StatusChannelInUse {
116 statusText = fmt.Sprintf("Waiting for channel to clear: %ds", v.WaitingForChannelSeconds)
117 disableCancel = false
118 } else if v.Status == protocol.StatusPlaying {
119 statusText = fmt.Sprintf("Playing: %d:%02d", v.PlaybackSecondsElapsed/60, v.PlaybackSecondsElapsed%60)
120 disableCancel = false
121 }
122 playlist := v.Playlist
123 if playlist == "" {
124 playlist = "-"
125 }
126 filename := v.Filename
127 if filename == "" {
128 filename = "-"
129 }
130 webStatuses = append(webStatuses, WebRadioStatus{
131 Name: radio.Name,
132 LocalTime: v.LocalTime,
133 TimeZone: v.TimeZone,
134 ChannelClass: channelClass,
135 ChannelState: channelState,
136 Playlist: playlist,
137 File: filename,
138 Status: statusText,
139 Id: strconv.Itoa(i),
140 DisableCancel: disableCancel,
141 FilesInSync: v.FilesInSync,
142 })
143 }
144 data := WebStatusData{
145 Radios: webStatuses,
146 }
147 buf := new(strings.Builder)
148 tmpl := template.Must(template.ParseFS(content, "templates/radios.partial.html"))
149 tmpl.Execute(buf, data)
150 _, err := ws.Write([]byte(buf.String()))
151 return err
152 }
153
154 func KeepWebUpdated(ws *websocket.Conn) {
155 for {
156 <-status.ChangeChannel()
157 err := sendRadioStatusToWeb(ws)
158 if err != nil {
159 return
160 }
161 }
162 }