diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | apiv1.go | 41 | ||||
| -rw-r--r-- | data.go | 32 | ||||
| -rw-r--r-- | migrations/20170613221502_np.sql | 21 | ||||
| -rw-r--r-- | static/np.gif | bin | 0 -> 1643 bytes | |||
| -rw-r--r-- | static/np2.gif | bin | 0 -> 1274 bytes | |||
| -rw-r--r-- | static/style.css | 7 | ||||
| -rw-r--r-- | templates/index.tmpl | 15 | ||||
| -rw-r--r-- | web.go | 13 |
9 files changed, 116 insertions, 17 deletions
@@ -16,10 +16,10 @@ watch: watchman-make -p '*.go' 'templates/*.tmpl' -t all up: - goose -dir migrations postgres "$(DB)" up + goose -dir migrations postgres $(DB) up down: - goose -dir migrations postgres "$(DB)" down + goose -dir migrations postgres $(DB) down status: goose -dir migrations postgres $(DB) status @@ -177,15 +177,14 @@ func (app *App) scrobbleHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "BADSESSION") return } - scrobbles, _ := parseScrobbles(r.Form, session) + scrobbles, _ := parseScrobbles(r.Form, session) for i, s := range scrobbles { app.GetSong(&s) scrobbles[i] = s } - err = app.PutScrobbles(scrobbles) - if err != nil { + if err := app.PutScrobbles(scrobbles); err != nil { log.Println(err) fmt.Fprintln(w, "Failed Database") return @@ -193,10 +192,40 @@ func (app *App) scrobbleHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") } +func parseNowPlaying(values url.Values) (Scrobble, error) { + var scrobble Scrobble + scrobble.TrackName = NewCorrectable(values.Get("t")) + scrobble.Artist = NewCorrectable(values.Get("a")) + 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 + } + return scrobble, nil +} + func (app *App) nowPlayingHandler(w http.ResponseWriter, r *http.Request) { - if _, err := app.GetSession(r.FormValue("s")); err != nil { + session, err := app.GetSession(r.FormValue("s")) + if err != nil { + log.Println(err) fmt.Fprintln(w, "BADSESSION") - } else { - fmt.Fprintln(w, "OK") + return + } + s, err := parseNowPlaying(r.Form) + s.SessionKey = session.Key + s.UserId = session.UserId + app.GetSong(&s) + if err := app.PutNowPlaying(s); err != nil { + log.Println(err) + fmt.Fprintln(w, "Failed Database") + return } + fmt.Fprintln(w, "OK") } @@ -39,6 +39,8 @@ type DataStore interface { PutSession(*Session) error GetSession(key string) (*Session, error) PutScrobbles([]Scrobble) error + PutNowPlaying(s Scrobble) error + NowPlaying(userId int) *Scrobble RecentScrobbles(userId int) []*Scrobble GetSongId(artist, album, name string) (int, error) InsertSong(s *Scrobble) (int, error) @@ -111,6 +113,22 @@ func (store *SqlStore) PutScrobbles(scrobbles []Scrobble) error { return tx.Commit() } +func (store *SqlStore) PutNowPlaying(s Scrobble) error { + query := ` + INSERT INTO now_playing + (artist, album_artist, track_name, album, track_number, duration, + mbid, song_id, session_key, user_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + ON CONFLICT (user_id) DO UPDATE + set artist=$1, album_artist=$2, track_name=$3, album=$4, track_number=$5, + duration=$6, mbid=$7, song_id=$8, session_key=$9, user_id=$10, + received=current_timestamp` + + _, err := store.Exec(query, s.Artist, s.AlbumArtist, s.TrackName, s.Album, + s.TrackNumber, s.Duration, s.Mbid, s.SongId, s.SessionKey, s.UserId) + return err +} + func (store *SqlStore) RecentScrobbles(userId int) []*Scrobble { scrobbles := make([]*Scrobble, 0, 10) query := ` @@ -134,6 +152,20 @@ func (store *SqlStore) RecentScrobbles(userId int) []*Scrobble { return scrobbles } +func (store *SqlStore) NowPlaying(userId int) *Scrobble { + scrobble := new(Scrobble) + query := ` + SELECT s.artist, s.album, s.track_name, s.received, songs.image, s.duration + FROM now_playing s + LEFT JOIN songs ON songs.song_id = s.song_id + WHERE user_id=$1` + row := store.QueryRow(query, userId) + row.Scan(&scrobble.Artist.Name, &scrobble.Album.Name, + &scrobble.TrackName.Name, &scrobble.Time, &scrobble.Image, + &scrobble.Duration) + return scrobble +} + func (store *SqlStore) GetSongId(artist, album, song string) (int, error) { query := ` SELECT song_id FROM songs diff --git a/migrations/20170613221502_np.sql b/migrations/20170613221502_np.sql new file mode 100644 index 0000000..7659555 --- /dev/null +++ b/migrations/20170613221502_np.sql @@ -0,0 +1,21 @@ + +-- +goose Up +-- SQL in section 'Up' is executed when this migration is applied +CREATE TABLE IF NOT EXISTS now_playing ( + artist text, + album_artist text, + track_name text, + album text, + track_number int, + duration int, + received timestamptz DEFAULT current_timestamp, + mbid text, + song_id int REFERENCES songs, + session_key text REFERENCES scrobbling_sessions, + user_id int UNIQUE REFERENCES users +); + +-- +goose Down +-- SQL section 'Down' is executed when this migration is rolled back +DROP TABLE now_playing; + diff --git a/static/np.gif b/static/np.gif Binary files differnew file mode 100644 index 0000000..7b53c2e --- /dev/null +++ b/static/np.gif diff --git a/static/np2.gif b/static/np2.gif Binary files differnew file mode 100644 index 0000000..91abce7 --- /dev/null +++ b/static/np2.gif diff --git a/static/style.css b/static/style.css index c4d2db4..4072988 100644 --- a/static/style.css +++ b/static/style.css @@ -68,7 +68,7 @@ ul.scrobbles li:hover { cursor: pointer; } -ul.scrobbles li img { +ul.scrobbles li img.cover { height: 40px; margin-right: 16px; border: 1px solid #C5CAE9; @@ -82,6 +82,11 @@ ul.scrobbles li div { line-height: 18px; } +ul.scrobbles li .time { + font-size: 12px; + color: #4f4f4f; +} + ul.scrobbles li .album { font-size: 12px; color: #4f4f4f; diff --git a/templates/index.tmpl b/templates/index.tmpl index 2451c6b..1ab1726 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -1,21 +1,26 @@ {{template "header" . }} <h2>Recent Listens</h2> <ul class="scrobbles"> + {{if .NowPlaying}} {{template "song" .NowPlaying}} {{end}} {{- range .Scrobbles}} + {{template "song" .}} +{{- end}} +</ul> +{{template "footer" }} + +{{define "song"}} <li> - <img src="{{or .Image "https://lastfm-img2.akamaized.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.png"}}"/> + <img class="cover" src="{{or .Image "https://lastfm-img2.akamaized.net/i/u/64s/4128a6eb29f94943c9d206c08e625904.png"}}"/> <div> {{.Artist}} — {{.TrackName}}<br/> <span class="album">in {{.Album}}</span> </div> <div class="like"> - <span class="album">{{ago .Time}}</span><br/> + <span class="time">{{.Time | ago}}</span><br/> <span class="swap"> <i class="material-icons one">favorite_border</i> <i class="material-icons two">favorite</i> </span> </div> </li> -{{- end}} -</ul> -{{template "footer" }} +{{end}} @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "net/http" + "time" _ "github.com/lib/pq" ) @@ -49,11 +50,17 @@ func (app *App) root(w http.ResponseWriter, r *http.Request) { } scrobbles := app.RecentScrobbles(se.UserId) + np := app.NowPlaying(se.UserId) + np.Image = "static/np2.gif" + if time.Since(np.Time) > time.Duration(np.Duration)*time.Second { + np = nil + } app.Template.ExecuteTemplate(w, "index.tmpl", struct { - Session *UserSession - Scrobbles []*Scrobble - }{se, scrobbles}) + Session *UserSession + Scrobbles []*Scrobble + NowPlaying *Scrobble + }{se, scrobbles, np}) } func (app *App) callback(w http.ResponseWriter, r *http.Request) { |
