aboutsummaryrefslogtreecommitdiffstats
path: root/main.go
diff options
context:
space:
mode:
authorThibaut Horel <thibaut.horel@gmail.com>2016-11-19 17:35:07 -0500
committerThibaut Horel <thibaut.horel@gmail.com>2016-11-19 17:35:07 -0500
commitc2c39cc2756230c1de29d8065b8b320f2f084045 (patch)
treebaf3569f25b0fabeb93144d636bf61a2deae6b4a /main.go
downloadlastfm-api-c2c39cc2756230c1de29d8065b8b320f2f084045.tar.gz
Initial commit
Diffstat (limited to 'main.go')
-rw-r--r--main.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..786263d
--- /dev/null
+++ b/main.go
@@ -0,0 +1,161 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "sort"
+ "strconv"
+ "time"
+)
+
+func mainHandler(ds DataStore, w http.ResponseWriter, r *http.Request) {
+ r.ParseForm()
+ if _, ok := r.Form["hs"]; ok {
+
+ protocol := r.FormValue("p")
+ if protocol != "1.2.1" && protocol != "1.2" {
+ fmt.Fprintf(w, "FAILED Protocol mismatch\n")
+ return
+ }
+
+ timestamp := r.FormValue("t")
+ ts, err := strconv.ParseInt(timestamp, 10, 0)
+ if err != nil {
+ fmt.Fprintf(w, "FAILED Invalid timestamp\n")
+ return
+ }
+
+ delta := time.Now().Unix() - ts
+ if delta > 30 || delta < -30 {
+ fmt.Fprintf(w, "BADTIME\n")
+ return
+ }
+
+ user := r.FormValue("u")
+ auth := r.FormValue("a")
+ password, ok := ds.GetPassword(user)
+
+ if (md5hex(password+timestamp) != auth) || !ok {
+ fmt.Fprintf(w, "BADAUTH\n")
+ return
+ }
+
+ client := r.FormValue("c")
+ s := NewSession(user, client, protocol)
+ ds.PutSession(s)
+ fmt.Fprint(w, "OK\n")
+ fmt.Fprintf(w, "%s\n", s.Key)
+ fmt.Fprint(w, "http://post.audioscrobbler.com:80/np\n")
+ fmt.Fprint(w, "http://post.audioscrobbler.com:80/scrobble\n")
+ } else {
+ fmt.Fprintf(w, "<html>This is an endpoint, see <a href=\"http://www.last.fm/api/submissions\">here</a></html>")
+ }
+}
+
+func parseValues(values url.Values) map[int]url.Values {
+ parts := make(map[int]url.Values)
+ var letter string
+ var idx int
+ for key, value := range values {
+ _, err := fmt.Sscanf(key, "%1s[%d]", &letter, &idx)
+ if err != nil {
+ continue
+ }
+ if _, ok := parts[idx]; !ok {
+ parts[idx] = make(url.Values)
+ }
+ parts[idx][letter] = value
+ }
+ return parts
+}
+
+func parsePart(values url.Values) (Scrobble, error) {
+ var scrobble Scrobble
+ scrobble.TrackName = values.Get("t")
+ scrobble.Artist = values.Get("a")
+ time, err := strconv.Atoi(values.Get("i"))
+ if err != nil {
+ return scrobble, errors.New("Could not parse timestamp")
+ }
+ scrobble.Time = time
+ scrobble.Album = values.Get("b")
+ scrobble.Mbid = values.Get("m")
+ tn, err := strconv.Atoi(values.Get("n"))
+ if err != nil {
+ return scrobble, errors.New("Could not parse track number")
+ }
+ scrobble.TrackNumber = tn
+ duration, err := strconv.Atoi(values.Get("l"))
+ if err != nil {
+ return scrobble, errors.New("Could not parse duration")
+ }
+ scrobble.Duration = duration
+ chosen := values.Get("o")
+ if chosen == "P" || chosen == "" {
+ scrobble.Chosen = true
+ }
+ return scrobble, nil
+}
+
+func parseScrobbles(values url.Values, session *Session) []Scrobble {
+ scrobbles := make([]Scrobble, 0, 1)
+ parts := parseValues(values)
+ keys := make([]int, len(parts))
+ i := 0
+ for key := range parts {
+ keys[i] = key
+ i++
+ }
+ sort.Ints(keys)
+ for _, key := range keys {
+ scrobble, err := parsePart(parts[key])
+ if err != nil {
+ continue
+ }
+ scrobble.Session = session.Key
+ scrobbles = append(scrobbles, scrobble)
+ }
+
+ return scrobbles
+}
+
+func scrobbleHandler(ds DataStore, w http.ResponseWriter, r *http.Request) {
+ if session, ok := ds.GetSession(r.FormValue("s")); ok {
+ scrobbles := parseScrobbles(r.Form, session)
+ ds.PutScrobbles(scrobbles)
+ fmt.Fprintf(w, "OK\n")
+ } else {
+ fmt.Fprintf(w, "BADSESSION\n")
+ }
+}
+
+func nowPlayingHandler(ds DataStore, w http.ResponseWriter, r *http.Request) {
+ if _, ok := ds.GetSession(r.FormValue("s")); ok {
+ fmt.Fprintf(w, "OK\n")
+ } else {
+ fmt.Fprintf(w, "BADSESSION\n")
+ }
+}
+
+type handler func(DataStore, http.ResponseWriter, *http.Request)
+
+func wrap(ds DataStore, fn handler) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ d, _ := httputil.DumpRequest(r, true)
+ fmt.Printf("--------------------------\n%s\n", d)
+ r.ParseForm()
+ fn(ds, w, r)
+ }
+}
+
+func main() {
+ store := NewSqlStore()
+ http.HandleFunc("/", wrap(store, handler(mainHandler)))
+ http.HandleFunc("/np", wrap(store, nowPlayingHandler))
+ http.HandleFunc("/scrobble", wrap(store, scrobbleHandler))
+ http.HandleFunc("/2.0/", wrap(store, ApiHandler))
+ http.ListenAndServe(":3001", nil)
+}