diff options
Diffstat (limited to 'apiv1.go')
| -rw-r--r-- | apiv1.go | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/apiv1.go b/apiv1.go new file mode 100644 index 0000000..f36f022 --- /dev/null +++ b/apiv1.go @@ -0,0 +1,192 @@ +package main + +import ( + "errors" + "fmt" + "log" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +func (app *App) mainHandler(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.Fprintln(w, "FAILED Protocol mismatch") + return + } + + timestamp := r.FormValue("t") + ts, err := strconv.ParseInt(timestamp, 10, 0) + if err != nil { + fmt.Fprintln(w, "FAILED Invalid timestamp") + return + } + + delta := time.Now().Unix() - ts + if delta > 30 || delta < -30 { + fmt.Fprintln(w, "BADTIME") + return + } + + user := r.FormValue("u") + auth := r.FormValue("a") + password, err := app.GetPassword(user) + if (md5hex(password+timestamp) != auth) || err != nil { + fmt.Fprintln(w, "BADAUTH") + return + } + + client := r.FormValue("c") + s := NewSession(user, client, protocol) + app.PutSession(s) + fmt.Fprintln(w, "OK") + fmt.Fprintf(w, "%s\n", s.Key) + fmt.Fprintln(w, "http://post.audioscrobbler.com:80/np") + fmt.Fprintln(w, "http://post.audioscrobbler.com:80/scrobble") + } 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 field string + for key, value := range values { + splitted_key := strings.SplitN(key, "[", 2) + if len(splitted_key) < 2 { + continue + } + field = splitted_key[0] + idx, err := strconv.Atoi(strings.TrimSuffix(splitted_key[1], "]")) + if err != nil { + continue + } else { + if _, ok := parts[idx]; !ok { + parts[idx] = make(url.Values) + } + parts[idx][field] = value + } + } + return parts +} + +func parsePart1(values url.Values) (Scrobble, error) { + var scrobble Scrobble + scrobble.TrackName = NewCorrectable(values.Get("t")) + scrobble.Artist = NewCorrectable(values.Get("a")) + if t, err := strconv.Atoi(values.Get("i")); err != nil { + return scrobble, errors.New("Could not parse timestamp") + } else { + scrobble.Time = time.Unix(int64(t), 0) + } + scrobble.Album = NewCorrectable(values.Get("b")) + scrobble.Mbid = values.Get("m") + if tn, err := strconv.Atoi(values.Get("n")); err != nil { + return scrobble, errors.New("Could not parse track number") + } else { + scrobble.TrackNumber = tn + } + if duration, err := strconv.Atoi(values.Get("l")); err != nil { + return scrobble, errors.New("Could not parse duration") + } else { + scrobble.Duration = duration + } + chosen := values.Get("o") + if chosen == "P" || chosen == "" { + scrobble.Chosen = true + } + return scrobble, nil +} + +func parsePart2(values url.Values) (Scrobble, error) { + var scrobble Scrobble + scrobble.TrackName = NewCorrectable(values.Get("track")) + scrobble.Artist = NewCorrectable(values.Get("artist")) + if t, err := strconv.Atoi(values.Get("timestamp")); err != nil { + return scrobble, errors.New("Could not parse timestamp") + } else { + scrobble.Time = time.Unix(int64(t), 0) + } + scrobble.Album = NewCorrectable(values.Get("album")) + scrobble.Mbid = values.Get("mbid") + if tn, ok := values["trackNumber"]; ok { + if tn, err := strconv.Atoi(tn[0]); err != nil { + return scrobble, errors.New("Could not parse track number") + } else { + scrobble.TrackNumber = tn + } + } + if duration, err := strconv.Atoi(values.Get("duration")); err != nil { + return scrobble, errors.New("Could not parse duration") + } else { + scrobble.Duration = duration + } + chosen := values.Get("chosenByUser") + if chosen == "1" || chosen == "" { + scrobble.Chosen = true + } + return scrobble, nil +} + +func parseScrobbles(values url.Values, session *Session) ([]Scrobble, int) { + scrobbles := make([]Scrobble, 0, 1) + parts := parseValues(values) + var parsePart func(url.Values) (Scrobble, error) + if session.Protocol == "2.0" { + parsePart = parsePart2 + } else { + parsePart = parsePart1 + } + ignored := 0 + for i, c := 0, 0; i < 50 && c < len(parts); i++ { + if part, ok := parts[i]; ok { + c++ + if scrobble, err := parsePart(part); err != nil { + fmt.Printf("%v\n", err) + ignored++ + } else { + scrobble.Session = session.Key + scrobbles = append(scrobbles, scrobble) + } + } + } + return scrobbles, ignored +} + +func (app *App) scrobbleHandler(w http.ResponseWriter, r *http.Request) { + if session, err := app.GetSession(r.FormValue("s")); err != nil { + fmt.Fprintln(w, "BADSESSION") + } else { + scrobbles, _ := parseScrobbles(r.Form, session) + + for i, s := range scrobbles { + t := app.TrackInfo(s.TrackName.Name, s.Artist.Name) + s.Mbid = t.Mbid + scrobbles[i] = s + _, err = app.DB.Exec("INSERT INTO songs VALUES ($1, $2) "+ + "ON CONFLICT (mbid) DO UPDATE SET mbid=$1, image=$2", + t.Mbid, t.GetImage("medium")) + if err != nil { + log.Println(err) + } + } + + fmt.Printf("%v\n", scrobbles) + app.PutScrobbles(scrobbles) + fmt.Fprintln(w, "OK") + } +} + +func (app *App) nowPlayingHandler(w http.ResponseWriter, r *http.Request) { + if _, err := app.GetSession(r.FormValue("s")); err != nil { + fmt.Fprintln(w, "BADSESSION") + } else { + fmt.Fprintln(w, "OK") + } +} |
