123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- package main
-
- import (
- "github.com/zmb3/spotify"
- "html/template"
- "log"
- "net/http"
- "strconv"
- "strings"
- "time"
- )
-
- var (
- cachedTemplates = template.Must(template.ParseFiles("web/index.html", "web/round.html"))
- )
-
- func (s *WebService) serveTemplate(w http.ResponseWriter, data interface{}, templateName string) {
- var templates = cachedTemplates
- if s.noCache {
- templates = template.Must(template.ParseFiles("web/index.html", "web/round.html"))
- }
-
- err := templates.ExecuteTemplate(w, templateName, data)
- if err != nil {
- log.Println("Error while rendering template", err)
- http.Error(w, err.Error(), http.StatusInternalServerError)
- }
-
- }
-
- func (s *WebService) IndexHandler(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/" {
- http.Error(w, "Not Found", http.StatusNotFound)
- return
- }
-
- data := struct {
- Username string
- Spotify string
- SpotifyExpiry string
- Songs []*Song
- }{
- "",
- "",
- "",
- make([]*Song, 0),
- }
-
- session, _ := getSession(r)
- if session != nil {
- log.Println("Find all songs for user", session.username)
- songs, err := s.db.FindAllEntries(session.username)
- if err != nil {
- log.Println("Error while reading entries from database", err)
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- data.Songs = songs
- data.Username = session.username
- data.Spotify = session.spotify
-
- if session.spotifyClient != nil {
- token, err := session.spotifyClient.Token()
- if err == nil {
- data.SpotifyExpiry = token.Expiry.Format(time.RFC822Z)
- }
- }
- }
- s.serveTemplate(w, data, "index.html")
- }
-
- func getTrackID(url string) string {
- const externalURLPrefix = "https://open.spotify.com/track/"
- const trackURIPrefix = "spotify:track:"
- if strings.HasPrefix(url, externalURLPrefix) {
- return strings.Split(url, externalURLPrefix)[1]
- } else if strings.HasPrefix(url, trackURIPrefix) {
- return strings.Split(url, trackURIPrefix)[1]
- }
- return url
- }
-
- func (s *WebService) UpdateHandler(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/update" {
- http.Error(w, "Forbidden", http.StatusForbidden)
- return
- }
- session, err := getSession(r)
- if session == nil {
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- return
- }
-
- err = r.ParseForm()
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- round := r.Form.Get("round")
- artist := r.Form.Get("artist")
- title := r.Form.Get("title")
- url := r.Form.Get("url")
-
- if artist == "" && title == "" && url != "" {
- var client = s.spotify.client
- if client == nil {
- client = session.spotifyClient
- }
- if client == nil {
- http.Error(w, "No Spotify token available", http.StatusInternalServerError)
- return
- }
- token, err := client.Token()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- } else if token.Expiry.Before(time.Now()) {
- http.Error(w, "Spotify token expired", http.StatusInternalServerError)
- return
- }
-
- log.Println("Resolving Spotify URL")
- trackID := getTrackID(url)
- track, err := client.GetTrack(spotify.ID(trackID))
-
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- log.Printf("Found Track with name %v and artists %v\n", track.Name, track.Artists)
- for index, trackArtist := range track.Artists {
- if index == 0 {
- artist = trackArtist.Name
- } else if index == 1 {
- artist = artist + " feat. " + trackArtist.Name
- } else {
- artist = artist + ", " + trackArtist.Name
- }
- }
- title = track.Name
- url = track.ExternalURLs["spotify"]
- }
-
- updated, err := s.db.UpdateEntry(session.username, round, artist, title, url)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if !updated {
- http.Error(w, "No round in DB", http.StatusNotFound)
- return
- }
-
- log.Printf("Updated song for round %s, artist: %s, title: %s, URL: %s",
- round, artist, title, url)
-
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- }
-
- func (s *WebService) LoginHandler(w http.ResponseWriter, r *http.Request) {
- err := r.ParseForm()
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- username := r.Form.Get("username")
- password := r.Form.Get("password")
- remember := r.Form.Get("remember")
- longer := remember == "remember-me"
-
- cookie, err := tryLogin(s.db, username, password, longer)
-
- if err != nil {
- log.Println("Error while trying to login", err.Error())
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- return
- }
-
- http.SetCookie(w, &cookie)
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- }
-
- func (s *WebService) SpotifyHandler(w http.ResponseWriter, r *http.Request) {
- url := s.spotify.auth.AuthURL("foobar")
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- }
-
- func (s *WebService) CallbackHandler(w http.ResponseWriter, r *http.Request) {
- state := "foobar"
- token, err := s.spotify.auth.Token(state, r)
- if err != nil {
- http.Error(w, "Couldn't get token", http.StatusForbidden)
- return
- }
- client := s.spotify.auth.NewClient(token)
- profile, err := client.CurrentUser()
- if err != nil {
- http.Error(w, "Couldn't load profile from Spotify", http.StatusInternalServerError)
- return
- }
- session, err := getSession(r)
- if session == nil {
- if err != nil {
- log.Println("Error while trying to read current session", err)
- }
- log.Println("Trying to login with spotify ID", profile.ID)
- cookie, err := tryLoginWithSpotify(s.db, profile.ID)
- if err != nil {
- http.Error(w, "Couldn't login with Spotify profile", http.StatusInternalServerError)
- return
- }
- session, _ := getSessionById(cookie.Value)
- session.spotifyClient = &client
-
- log.Println("Logged in user spotify ID", profile.ID)
- http.SetCookie(w, &cookie)
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- return
- } else {
- session.spotify = profile.ID
- session.spotifyClient = &client
- http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
- return
- }
- // TODO: when?
- if profile.ID == "lamperi" {
- s.spotify.client = &client
- }
- }
-
- func (s *WebService) RoundHandler(w http.ResponseWriter, r *http.Request) {
- data := struct {
- Username string
- Spotify string
- SpotifyExpiry string
- Songs []*Song
- SubmitterPublic bool
- RoundInfo *RoundInfo
- }{
- "",
- "",
- "",
- make([]*Song, 0),
- false,
- nil,
- }
-
- round := strings.TrimPrefix(r.URL.Path, "/round/")
- roundNum, err := strconv.Atoi(round)
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- roundInfo, err := s.db.FindRoundInfo(roundNum)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if roundInfo == nil {
- http.Error(w, "No such panel", http.StatusNotFound)
- return
- }
- data.RoundInfo = roundInfo
- session, err := getSession(r)
- if session != nil {
- data.Username = session.username
- data.Spotify = session.spotify
- }
- songs, err := s.db.FindAllRoundEntries(roundNum)
- if err != nil {
- log.Println("Error while reading entries from database", err)
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- data.Songs = songs
- data.SubmitterPublic = roundInfo.End.Before(time.Now())
-
- if s.spotify.client != nil {
- token, err := s.spotify.client.Token()
- if err == nil {
- data.SpotifyExpiry = token.Expiry.Format(time.RFC822Z)
- }
- }
- s.serveTemplate(w, data, "round.html")
- }
-
- type WebService struct {
- db *DB
- spotify *AppSpotify
- noCache bool
- }
-
- func webStart(listenAddr string, db *DB, spotify *AppSpotify) {
- service := &WebService{db, spotify, true}
-
- mux := http.NewServeMux()
-
- fs := http.FileServer(http.Dir("web"))
- mux.Handle("/css/", fs)
- mux.Handle("/fonts/", fs)
- mux.Handle("/js/", fs)
- mux.Handle("/favicon.ico", fs)
- mux.HandleFunc("/", service.IndexHandler)
- mux.HandleFunc("/round/", service.RoundHandler)
- mux.HandleFunc("/update", service.UpdateHandler)
- mux.HandleFunc("/login", service.LoginHandler)
- mux.HandleFunc("/spotify", service.SpotifyHandler)
- mux.HandleFunc("/callback", service.CallbackHandler)
-
- http.ListenAndServe(listenAddr, mux)
- }
|