5 "golang.org/x/net/websocket"
17 const formatString
= "2006-01-02T15:04"
19 var config ServerConfig
= NewServerConfig()
22 configFlag
:= flag
.String("c", "", "path to configuration file")
24 //generateFlag := flag.String("g", "", "create a template config file with specified name then exit")
27 if *configFlag
== "" {
28 log
.Fatal("must specify a configuration file with -c")
30 config
.LoadFromFile(*configFlag
)
32 log
.Println("Hello, World! Woo broadcast time")
34 defer db
.CloseDatabase()
37 InitAudioFiles(config
.AudioFilesPath
)
40 http
.HandleFunc("/", homePage
)
41 http
.HandleFunc("/login", logInPage
)
42 http
.HandleFunc("/logout", logOutPage
)
43 http
.HandleFunc("/secret", secretPage
)
45 http
.HandleFunc("/playlist/", playlistSection
)
46 http
.HandleFunc("/file/", fileSection
)
47 http
.HandleFunc("/radio/", radioSection
)
49 http
.Handle("/radiosync", websocket
.Handler(RadioSync
))
50 http
.Handle("/websync", websocket
.Handler(WebSync
))
51 http
.Handle("/audio-files/", http
.StripPrefix("/audio-files/", http
.FileServer(http
.Dir(config
.AudioFilesPath
))))
53 err
:= http
.ListenAndServe(config
.BindAddress
+":"+strconv
.Itoa(config
.Port
), nil)
59 type HomeData
struct {
64 func homePage(w http
.ResponseWriter
, r
*http
.Request
) {
65 tmpl
:= template
.Must(template
.ParseFiles("templates/index.html"))
73 func secretPage(w http
.ResponseWriter
, r
*http
.Request
) {
74 user
, err
:= currentUser(w
, r
)
76 http
.Redirect(w
, r
, "/login", http
.StatusFound
)
79 tmpl
:= template
.Must(template
.ParseFiles("templates/index.html"))
82 Username
: user
.username
+ ", you are special",
87 type LogInData
struct {
91 func logInPage(w http
.ResponseWriter
, r
*http
.Request
) {
92 log
.Println("Log in page!")
94 username
:= r
.Form
["username"]
95 password
:= r
.Form
["password"]
98 log
.Println("Looks like we have a username", username
[0])
99 if username
[0] == "admin" && password
[0] == "test" {
100 createSessionCookie(w
)
101 http
.Redirect(w
, r
, "/", http
.StatusFound
)
104 err
= "Incorrect login"
112 tmpl
:= template
.Must(template
.ParseFiles("templates/login.html"))
113 tmpl
.Execute(w
, data
)
116 func playlistSection(w http
.ResponseWriter
, r
*http
.Request
) {
117 path
:= strings
.Split(r
.URL
.Path
, "/")
122 if path
[2] == "new" {
123 editPlaylistPage(w
, r
, 0)
124 } else if path
[2] == "submit" && r
.Method
== "POST" {
126 } else if path
[2] == "delete" && r
.Method
== "POST" {
128 } else if path
[2] == "" {
131 id
, err
:= strconv
.Atoi(path
[2])
136 editPlaylistPage(w
, r
, id
)
140 func fileSection(w http
.ResponseWriter
, r
*http
.Request
) {
141 path
:= strings
.Split(r
.URL
.Path
, "/")
146 if path
[2] == "upload" {
148 } else if path
[2] == "delete" && r
.Method
== "POST" {
150 } else if path
[2] == "" {
158 func radioSection(w http
.ResponseWriter
, r
*http
.Request
) {
159 path
:= strings
.Split(r
.URL
.Path
, "/")
164 if path
[2] == "new" {
165 editRadioPage(w
, r
, 0)
166 } else if path
[2] == "submit" && r
.Method
== "POST" {
168 } else if path
[2] == "delete" && r
.Method
== "POST" {
170 } else if path
[2] == "" {
173 id
, err
:= strconv
.Atoi(path
[2])
178 editRadioPage(w
, r
, id
)
182 type PlaylistsPageData
struct {
186 func playlistsPage(w http
.ResponseWriter
, _
*http
.Request
) {
187 data
:= PlaylistsPageData
{
188 Playlists
: db
.GetPlaylists(),
190 tmpl
:= template
.Must(template
.ParseFiles("templates/playlists.html"))
191 err
:= tmpl
.Execute(w
, data
)
197 type RadiosPageData
struct {
201 func radiosPage(w http
.ResponseWriter
, _
*http
.Request
) {
202 data
:= RadiosPageData
{
203 Radios
: db
.GetRadios(),
205 tmpl
:= template
.Must(template
.ParseFiles("templates/radios.html"))
206 err
:= tmpl
.Execute(w
, data
)
212 type EditPlaylistPageData
struct {
214 Entries
[]PlaylistEntry
218 func editPlaylistPage(w http
.ResponseWriter
, r
*http
.Request
, id
int) {
219 var data EditPlaylistPageData
220 for _
, f
:= range files
.Files() {
221 data
.Files
= append(data
.Files
, f
.Name
)
224 data
.Playlist
.Enabled
= true
225 data
.Playlist
.Name
= "New Playlist"
226 data
.Playlist
.StartTime
= time
.Now().Format(formatString
)
227 data
.Entries
= append(data
.Entries
, PlaylistEntry
{})
229 playlist
, err
:= db
.GetPlaylist(id
)
234 data
.Playlist
= playlist
235 data
.Entries
= db
.GetEntriesForPlaylist(id
)
237 tmpl
:= template
.Must(template
.ParseFiles("templates/playlist.html"))
238 tmpl
.Execute(w
, data
)
241 func submitPlaylist(w http
.ResponseWriter
, r
*http
.Request
) {
245 id
, err
:= strconv
.Atoi(r
.Form
.Get("playlistId"))
249 _
, err
= time
.Parse(formatString
, r
.Form
.Get("playlistStartTime"))
254 p
.Enabled
= r
.Form
.Get("playlistEnabled") == "1"
255 p
.Name
= r
.Form
.Get("playlistName")
256 p
.StartTime
= r
.Form
.Get("playlistStartTime")
258 delays
:= r
.Form
["delaySeconds"]
259 filenames
:= r
.Form
["filename"]
260 isRelatives
:= r
.Form
["isRelative"]
262 entries
:= make([]PlaylistEntry
, 0)
263 for i
:= range delays
{
265 delay
, err
:= strconv
.Atoi(delays
[i
])
269 e
.DelaySeconds
= delay
271 e
.IsRelative
= isRelatives
[i
] == "1"
272 e
.Filename
= filenames
[i
]
273 entries
= append(entries
, e
)
275 cleanedEntries
:= make([]PlaylistEntry
, 0)
276 for _
, e
:= range entries
{
277 if e
.DelaySeconds
!= 0 || e
.Filename
!= "" {
278 cleanedEntries
= append(cleanedEntries
, e
)
285 id
= db
.CreatePlaylist(p
)
287 db
.SetEntriesForPlaylist(cleanedEntries
, id
)
288 // Notify connected radios
289 playlists
.NotifyChanges()
291 http
.Redirect(w
, r
, "/playlist/", http
.StatusFound
)
294 func deletePlaylist(w http
.ResponseWriter
, r
*http
.Request
) {
297 id
, err
:= strconv
.Atoi(r
.Form
.Get("playlistId"))
301 db
.DeletePlaylist(id
)
302 playlists
.NotifyChanges()
304 http
.Redirect(w
, r
, "/playlist/", http
.StatusFound
)
307 type EditRadioPageData
struct {
311 func editRadioPage(w http
.ResponseWriter
, r
*http
.Request
, id
int) {
312 var data EditRadioPageData
314 data
.Radio
.Name
= "New Radio"
315 data
.Radio
.Token
= generateSession()
317 radio
, err
:= db
.GetRadio(id
)
324 tmpl
:= template
.Must(template
.ParseFiles("templates/radio.html"))
325 tmpl
.Execute(w
, data
)
328 func submitRadio(w http
.ResponseWriter
, r
*http
.Request
) {
332 id
, err
:= strconv
.Atoi(r
.Form
.Get("radioId"))
337 radio
.Name
= r
.Form
.Get("radioName")
338 radio
.Token
= r
.Form
.Get("radioToken")
340 db
.UpdateRadio(radio
)
342 db
.CreateRadio(radio
)
345 http
.Redirect(w
, r
, "/radio/", http
.StatusFound
)
348 func deleteRadio(w http
.ResponseWriter
, r
*http
.Request
) {
351 id
, err
:= strconv
.Atoi(r
.Form
.Get("radioId"))
357 http
.Redirect(w
, r
, "/radio/", http
.StatusFound
)
360 type FilesPageData
struct {
364 func filesPage(w http
.ResponseWriter
, _
*http
.Request
) {
365 data
:= FilesPageData
{
366 Files
: files
.Files(),
368 log
.Println("file page data", data
)
369 tmpl
:= template
.Must(template
.ParseFiles("templates/files.html"))
370 err
:= tmpl
.Execute(w
, data
)
376 func deleteFile(w http
.ResponseWriter
, r
*http
.Request
) {
379 filename
:= r
.Form
.Get("filename")
383 files
.Delete(filename
)
385 http
.Redirect(w
, r
, "/file/", http
.StatusFound
)
388 func uploadFile(w http
.ResponseWriter
, r
*http
.Request
) {
389 err
:= r
.ParseMultipartForm(100 << 20)
390 file
, handler
, err
:= r
.FormFile("file")
392 path
:= filepath
.Join(files
.Path(), filepath
.Base(handler
.Filename
))
393 f
, _
:= os
.Create(path
)
396 log
.Println("uploaded file to", path
)
399 http
.Redirect(w
, r
, "/file/", http
.StatusFound
)
402 func logOutPage(w http
.ResponseWriter
, r
*http
.Request
) {
403 clearSessionCookie(w
)
404 tmpl
:= template
.Must(template
.ParseFiles("templates/logout.html"))