web.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package main
  2. import (
  3. "github.com/zmb3/spotify"
  4. "html/template"
  5. "log"
  6. "net/http"
  7. "strings"
  8. "time"
  9. )
  10. var (
  11. cachedTemplates = template.Must(template.ParseFiles("web/index.html"))
  12. )
  13. func (s *WebService) IndexHandler(w http.ResponseWriter, r *http.Request) {
  14. if r.URL.Path != "/" {
  15. http.Error(w, "Not Found", http.StatusNotFound)
  16. return
  17. }
  18. data := struct {
  19. Username string
  20. Spotify string
  21. SpotifyExpiry string
  22. Songs []*Song
  23. }{
  24. "",
  25. "",
  26. "",
  27. make([]*Song, 0),
  28. }
  29. session, err := getSession(r)
  30. if session != nil {
  31. songs, err := s.db.FindAllEntries(session.username)
  32. if err != nil {
  33. log.Println("Error while reading entries from database", err)
  34. http.Error(w, err.Error(), http.StatusInternalServerError)
  35. return
  36. }
  37. data.Songs = songs
  38. data.Username = session.username
  39. data.Spotify = session.spotify
  40. }
  41. if s.spotify.client != nil {
  42. token, err := s.spotify.client.Token()
  43. if err == nil {
  44. data.SpotifyExpiry = token.Expiry.Format(time.RFC822Z)
  45. }
  46. }
  47. var templates = cachedTemplates
  48. if s.noCache {
  49. templates = template.Must(template.ParseFiles("web/index.html"))
  50. }
  51. err = templates.ExecuteTemplate(w, "index.html", data)
  52. if err != nil {
  53. log.Println("Error while rendering template", err)
  54. http.Error(w, err.Error(), http.StatusInternalServerError)
  55. }
  56. }
  57. func getTrackID(url string) string {
  58. const externalURLPrefix = "https://open.spotify.com/track/"
  59. const trackURIPrefix = "spotify:track:"
  60. if strings.HasPrefix(url, externalURLPrefix) {
  61. return strings.Split(url, externalURLPrefix)[1]
  62. } else if strings.HasPrefix(url, trackURIPrefix) {
  63. return strings.Split(url, trackURIPrefix)[1]
  64. }
  65. return url
  66. }
  67. func (s *WebService) UpdateHandler(w http.ResponseWriter, r *http.Request) {
  68. if r.URL.Path != "/update" {
  69. http.Error(w, "Forbidden", http.StatusForbidden)
  70. return
  71. }
  72. session, err := getSession(r)
  73. if session == nil {
  74. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  75. return
  76. }
  77. err = r.ParseForm()
  78. if err != nil {
  79. http.Error(w, err.Error(), http.StatusBadRequest)
  80. return
  81. }
  82. round := r.Form.Get("round")
  83. artist := r.Form.Get("artist")
  84. title := r.Form.Get("title")
  85. url := r.Form.Get("url")
  86. if artist == "" && title == "" && url != "" {
  87. if s.spotify.client == nil {
  88. http.Error(w, "No Spotify token available", http.StatusInternalServerError)
  89. return
  90. }
  91. token, err := s.spotify.client.Token()
  92. if err != nil {
  93. http.Error(w, err.Error(), http.StatusInternalServerError)
  94. return
  95. } else if token.Expiry.Before(time.Now()) {
  96. http.Error(w, "Spotify token expired", http.StatusInternalServerError)
  97. return
  98. }
  99. log.Println("Resolving Spotify URL")
  100. trackID := getTrackID(url)
  101. track, err := s.spotify.client.GetTrack(spotify.ID(trackID))
  102. if err != nil {
  103. http.Error(w, err.Error(), http.StatusInternalServerError)
  104. return
  105. }
  106. log.Printf("Found Track with name %v and artists %v\n", track.Name, track.Artists)
  107. for index, trackArtist := range track.Artists {
  108. if index == 0 {
  109. artist = trackArtist.Name
  110. } else if index == 1 {
  111. artist = artist + " feat. " + trackArtist.Name
  112. } else {
  113. artist = artist + ", " + trackArtist.Name
  114. }
  115. }
  116. title = track.Name
  117. url = track.ExternalURLs["spotify"]
  118. }
  119. updated, err := s.db.UpdateEntry(session.username, round, artist, title, url)
  120. if err != nil {
  121. http.Error(w, err.Error(), http.StatusInternalServerError)
  122. return
  123. }
  124. if !updated {
  125. http.Error(w, "No round in DB", http.StatusNotFound)
  126. return
  127. }
  128. log.Printf("Updated song for round %s, artist: %s, title: %s, URL: %s",
  129. round, artist, title, url)
  130. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  131. }
  132. func (s *WebService) LoginHandler(w http.ResponseWriter, r *http.Request) {
  133. err := r.ParseForm()
  134. if err != nil {
  135. http.Error(w, err.Error(), http.StatusBadRequest)
  136. return
  137. }
  138. username := r.Form.Get("username")
  139. password := r.Form.Get("password")
  140. remember := r.Form.Get("remember")
  141. longer := remember == "remember-me"
  142. cookie, err := tryLogin(s.db, username, password, longer)
  143. if err != nil {
  144. log.Println("Error while trying to login", err.Error())
  145. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  146. return
  147. }
  148. http.SetCookie(w, &cookie)
  149. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  150. }
  151. func (s *WebService) SpotifyHandler(w http.ResponseWriter, r *http.Request) {
  152. url := s.spotify.auth.AuthURL("foobar")
  153. http.Redirect(w, r, url, http.StatusTemporaryRedirect)
  154. }
  155. func (s *WebService) CallbackHandler(w http.ResponseWriter, r *http.Request) {
  156. state := "foobar"
  157. token, err := s.spotify.auth.Token(state, r)
  158. if err != nil {
  159. http.Error(w, "Couldn't get token", http.StatusForbidden)
  160. return
  161. }
  162. session, err := getSession(r)
  163. if err != nil {
  164. http.Error(w, "Couldn't get session", http.StatusInternalServerError)
  165. return
  166. }
  167. client := s.spotify.auth.NewClient(token)
  168. s.spotify.client = &client
  169. profile, err := s.spotify.client.CurrentUser()
  170. if err != nil {
  171. http.Error(w, "Couldn't load profile from Spotify", http.StatusInternalServerError)
  172. return
  173. }
  174. session.spotify = profile.ID
  175. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  176. }
  177. type WebService struct {
  178. db *DB
  179. spotify *AppSpotify
  180. noCache bool
  181. }
  182. func webStart(listenAddr string, db *DB, spotify *AppSpotify) {
  183. service := &WebService{db, spotify, true}
  184. mux := http.NewServeMux()
  185. fs := http.FileServer(http.Dir("web"))
  186. mux.Handle("/css/", fs)
  187. mux.Handle("/fonts/", fs)
  188. mux.Handle("/js/", fs)
  189. mux.Handle("/favicon.ico", fs)
  190. mux.HandleFunc("/", service.IndexHandler)
  191. mux.HandleFunc("/update", service.UpdateHandler)
  192. mux.HandleFunc("/login", service.LoginHandler)
  193. mux.HandleFunc("/spotify", service.SpotifyHandler)
  194. mux.HandleFunc("/callback", service.CallbackHandler)
  195. http.ListenAndServe(listenAddr, mux)
  196. }