5 "golang.org/x/net/websocket"
17 const formatString
= "2006-01-02T15:04"
19 var config ServerConfig
= NewServerConfig()
21 // Channel that will be closed and recreated every time the playlists change
22 var playlistChangeWait
= make(chan bool)
25 configFlag
:= flag
.String("c", "", "path to configuration file")
27 //generateFlag := flag.String("g", "", "create a template config file with specified name then exit")
30 if *configFlag
== "" {
31 log
.Fatal("must specify a configuration file with -c")
33 config
.LoadFromFile(*configFlag
)
35 log
.Println("Hello, World! Woo broadcast time")
37 defer db
.CloseDatabase()
39 InitAudioFiles(config
.AudioFilesPath
)
42 http
.HandleFunc("/", homePage
)
43 http
.HandleFunc("/login", logInPage
)
44 http
.HandleFunc("/logout", logOutPage
)
45 http
.HandleFunc("/secret", secretPage
)
47 http
.HandleFunc("/playlist/", playlistSection
)
48 http
.HandleFunc("/file/", fileSection
)
49 http
.HandleFunc("/radio/", radioSection
)
51 http
.Handle("/radiosync", websocket
.Handler(RadioSync
))
52 http
.Handle("/websync", websocket
.Handler(WebSync
))
53 http
.Handle("/audio-files/", http
.StripPrefix("/audio-files/", http
.FileServer(http
.Dir(config
.AudioFilesPath
))))
55 err
:= http
.ListenAndServe(config
.BindAddress
+":"+strconv
.Itoa(config
.Port
), nil)
61 type HomeData
struct {
66 func homePage(w http
.ResponseWriter
, r
*http
.Request
) {
67 tmpl
:= template
.Must(template
.ParseFiles("templates/index.html"))
75 func secretPage(w http
.ResponseWriter
, r
*http
.Request
) {
76 user
, err
:= currentUser(w
, r
)
78 http
.Redirect(w
, r
, "/login", http
.StatusFound
)
81 tmpl
:= template
.Must(template
.ParseFiles("templates/index.html"))
84 Username
: user
.username
+ ", you are special",
89 type LogInData
struct {
93 func logInPage(w http
.ResponseWriter
, r
*http
.Request
) {
94 log
.Println("Log in page!")
96 username
:= r
.Form
["username"]
97 password
:= r
.Form
["password"]
100 log
.Println("Looks like we have a username", username
[0])
101 if username
[0] == "admin" && password
[0] == "test" {
102 createSessionCookie(w
)
103 http
.Redirect(w
, r
, "/", http
.StatusFound
)
106 err
= "Incorrect login"
114 tmpl
:= template
.Must(template
.ParseFiles("templates/login.html"))
115 tmpl
.Execute(w
, data
)
118 func playlistSection(w http
.ResponseWriter
, r
*http
.Request
) {
119 path
:= strings
.Split(r
.URL
.Path
, "/")
124 if path
[2] == "new" {
125 editPlaylistPage(w
, r
, 0)
126 } else if path
[2] == "submit" && r
.Method
== "POST" {
128 } else if path
[2] == "delete" && r
.Method
== "POST" {
130 } else if path
[2] == "" {
133 id
, err
:= strconv
.Atoi(path
[2])
138 editPlaylistPage(w
, r
, id
)
142 func fileSection(w http
.ResponseWriter
, r
*http
.Request
) {
143 path
:= strings
.Split(r
.URL
.Path
, "/")
148 if path
[2] == "upload" {
150 } else if path
[2] == "delete" && r
.Method
== "POST" {
152 } else if path
[2] == "" {
160 func radioSection(w http
.ResponseWriter
, r
*http
.Request
) {
161 path
:= strings
.Split(r
.URL
.Path
, "/")
166 if path
[2] == "new" {
167 editRadioPage(w
, r
, 0)
168 } else if path
[2] == "submit" && r
.Method
== "POST" {
170 } else if path
[2] == "delete" && r
.Method
== "POST" {
172 } else if path
[2] == "" {
175 id
, err
:= strconv
.Atoi(path
[2])
180 editRadioPage(w
, r
, id
)
184 type PlaylistsPageData
struct {
188 func playlistsPage(w http
.ResponseWriter
, _
*http
.Request
) {
189 data
:= PlaylistsPageData
{
190 Playlists
: db
.GetPlaylists(),
192 tmpl
:= template
.Must(template
.ParseFiles("templates/playlists.html"))
193 err
:= tmpl
.Execute(w
, data
)
199 type RadiosPageData
struct {
203 func radiosPage(w http
.ResponseWriter
, _
*http
.Request
) {
204 data
:= RadiosPageData
{
205 Radios
: db
.GetRadios(),
207 tmpl
:= template
.Must(template
.ParseFiles("templates/radios.html"))
208 err
:= tmpl
.Execute(w
, data
)
214 type EditPlaylistPageData
struct {
216 Entries
[]PlaylistEntry
220 func editPlaylistPage(w http
.ResponseWriter
, r
*http
.Request
, id
int) {
221 var data EditPlaylistPageData
222 for _
, f
:= range files
.Files() {
223 data
.Files
= append(data
.Files
, f
.Name
)
226 data
.Playlist
.Enabled
= true
227 data
.Playlist
.Name
= "New Playlist"
228 data
.Playlist
.StartTime
= time
.Now().Format(formatString
)
229 data
.Entries
= append(data
.Entries
, PlaylistEntry
{})
231 playlist
, err
:= db
.GetPlaylist(id
)
236 data
.Playlist
= playlist
237 data
.Entries
= db
.GetEntriesForPlaylist(id
)
239 tmpl
:= template
.Must(template
.ParseFiles("templates/playlist.html"))
240 tmpl
.Execute(w
, data
)
243 func submitPlaylist(w http
.ResponseWriter
, r
*http
.Request
) {
247 id
, err
:= strconv
.Atoi(r
.Form
.Get("playlistId"))
251 _
, err
= time
.Parse(formatString
, r
.Form
.Get("playlistStartTime"))
256 p
.Enabled
= r
.Form
.Get("playlistEnabled") == "1"
257 p
.Name
= r
.Form
.Get("playlistName")
258 p
.StartTime
= r
.Form
.Get("playlistStartTime")
260 delays
:= r
.Form
["delaySeconds"]
261 filenames
:= r
.Form
["filename"]
262 isRelatives
:= r
.Form
["isRelative"]
264 entries
:= make([]PlaylistEntry
, 0)
265 for i
:= range delays
{
267 delay
, err
:= strconv
.Atoi(delays
[i
])
271 e
.DelaySeconds
= delay
273 e
.IsRelative
= isRelatives
[i
] == "1"
274 e
.Filename
= filenames
[i
]
275 entries
= append(entries
, e
)
277 cleanedEntries
:= make([]PlaylistEntry
, 0)
278 for _
, e
:= range entries
{
279 if e
.DelaySeconds
!= 0 || e
.Filename
!= "" {
280 cleanedEntries
= append(cleanedEntries
, e
)
287 id
= db
.CreatePlaylist(p
)
289 db
.SetEntriesForPlaylist(cleanedEntries
, id
)
290 // Notify connected radios
291 close(playlistChangeWait
)
292 playlistChangeWait
= make(chan bool)
294 http
.Redirect(w
, r
, "/playlist/", http
.StatusFound
)
297 func deletePlaylist(w http
.ResponseWriter
, r
*http
.Request
) {
300 id
, err
:= strconv
.Atoi(r
.Form
.Get("playlistId"))
304 db
.DeletePlaylist(id
)
306 http
.Redirect(w
, r
, "/playlist/", http
.StatusFound
)
309 type EditRadioPageData
struct {
313 func editRadioPage(w http
.ResponseWriter
, r
*http
.Request
, id
int) {
314 var data EditRadioPageData
316 data
.Radio
.Name
= "New Radio"
317 data
.Radio
.Token
= generateSession()
319 radio
, err
:= db
.GetRadio(id
)
326 tmpl
:= template
.Must(template
.ParseFiles("templates/radio.html"))
327 tmpl
.Execute(w
, data
)
330 func submitRadio(w http
.ResponseWriter
, r
*http
.Request
) {
334 id
, err
:= strconv
.Atoi(r
.Form
.Get("radioId"))
339 radio
.Name
= r
.Form
.Get("radioName")
340 radio
.Token
= r
.Form
.Get("radioToken")
342 db
.UpdateRadio(radio
)
344 db
.CreateRadio(radio
)
347 http
.Redirect(w
, r
, "/radio/", http
.StatusFound
)
350 func deleteRadio(w http
.ResponseWriter
, r
*http
.Request
) {
353 id
, err
:= strconv
.Atoi(r
.Form
.Get("radioId"))
359 http
.Redirect(w
, r
, "/radio/", http
.StatusFound
)
362 type FilesPageData
struct {
366 func filesPage(w http
.ResponseWriter
, _
*http
.Request
) {
367 data
:= FilesPageData
{
368 Files
: files
.Files(),
370 log
.Println("file page data", data
)
371 tmpl
:= template
.Must(template
.ParseFiles("templates/files.html"))
372 err
:= tmpl
.Execute(w
, data
)
378 func deleteFile(w http
.ResponseWriter
, r
*http
.Request
) {
381 filename
:= r
.Form
.Get("filename")
385 files
.Delete(filename
)
387 http
.Redirect(w
, r
, "/file/", http
.StatusFound
)
390 func uploadFile(w http
.ResponseWriter
, r
*http
.Request
) {
391 err
:= r
.ParseMultipartForm(100 << 20)
392 file
, handler
, err
:= r
.FormFile("file")
394 path
:= filepath
.Join(files
.Path(), filepath
.Base(handler
.Filename
))
395 f
, _
:= os
.Create(path
)
398 log
.Println("uploaded file to", path
)
401 http
.Redirect(w
, r
, "/file/", http
.StatusFound
)
404 func logOutPage(w http
.ResponseWriter
, r
*http
.Request
) {
405 clearSessionCookie(w
)
406 tmpl
:= template
.Must(template
.ParseFiles("templates/logout.html"))