summaryrefslogtreecommitdiffstats
path: root/apiv1.go
diff options
context:
space:
mode:
Diffstat (limited to 'apiv1.go')
-rw-r--r--apiv1.go192
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")
+ }
+}