diff --git a/docs/changelog.md b/docs/changelog.md index 1593139b..b6129e35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,7 @@ # 0.1.2 - Add docker-compose file for local dev - Implemented top listeners for artist/album endpoints to match track +- Add recent endpoint # 0.1.1 - Cached all config values diff --git a/internal/goscrobble/album.go b/internal/goscrobble/album.go index 716e12f2..a0340380 100644 --- a/internal/goscrobble/album.go +++ b/internal/goscrobble/album.go @@ -130,54 +130,56 @@ func getAlbumByUUID(uuid string) (Album, error) { } // getTopUsersForAlbumUUID - Returns list of top users for a track -func getTopUsersForAlbumUUID(trackUUID string, limit int, page int) (TopUserResponse, error) { +func getTopUsersForAlbumUUID(albumUUID string, limit int, page int) (TopUserResponse, error) { response := TopUserResponse{} - // TODO: Implement this - // var count int + var count int - // total, err := getDbCount( - // "SELECT COUNT(*) FROM `scrobbles` WHERE `album` = UUID_TO_BIN(?, true) GROUP BY `album`, `user`", trackUUID) + total, err := getDbCount( + "SELECT COUNT(*) FROM `scrobbles` "+ + "JOIN `track_album` ON `track_album`.`track` = `scrobbles`.`track` "+ + "WHERE `track_album`.`album` = UUID_TO_BIN(?, true);", albumUUID) - // if err != nil { - // log.Printf("Failed to fetch scrobble count: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } + if err != nil { + log.Printf("Failed to fetch scrobble count: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } - // rows, err := db.Query( - // "SELECT BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username`, COUNT(*) "+ - // "FROM `scrobbles` "+ - // "JOIN `users` ON `scrobbles`.`user` = `users`.`uuid` "+ - // "WHERE `track` = UUID_TO_BIN(?, true) "+ - // "GROUP BY `scrobbles`.`user` "+ - // "ORDER BY COUNT(*) DESC LIMIT ?", - // trackUUID, limit) + rows, err := db.Query( + "SELECT BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username`, COUNT(*) "+ + "FROM `scrobbles` "+ + "JOIN `users` ON `scrobbles`.`user` = `users`.`uuid` "+ + "JOIN `track_album` ON `track_album`.`track` = `scrobbles`.`track` "+ + "WHERE `track_album`.`album` = UUID_TO_BIN(?, true) "+ + "GROUP BY `scrobbles`.`user` "+ + "ORDER BY COUNT(*) DESC LIMIT ?", + albumUUID, limit) - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } - // defer rows.Close() + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } + defer rows.Close() - // for rows.Next() { - // item := TopUserResponseItem{} - // err := rows.Scan(&item.UserUUID, &item.UserName, &item.Count) - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } - // count++ - // response.Items = append(response.Items, item) - // } + for rows.Next() { + item := TopUserResponseItem{} + err := rows.Scan(&item.UserUUID, &item.UserName, &item.Count) + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } + count++ + response.Items = append(response.Items, item) + } - // err = rows.Err() - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch scrobbles") - // } + err = rows.Err() + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch scrobbles") + } - // response.Meta.Count = count - // response.Meta.Total = total - // response.Meta.Page = page + response.Meta.Count = count + response.Meta.Total = total + response.Meta.Page = page return response, nil } diff --git a/internal/goscrobble/artist.go b/internal/goscrobble/artist.go index 304ad531..9b33d9ba 100644 --- a/internal/goscrobble/artist.go +++ b/internal/goscrobble/artist.go @@ -166,56 +166,57 @@ func getTopArtists(userUuid string) (TopArtists, error) { } // getTopUsersForArtistUUID - Returns list of top users for a track -func getTopUsersForArtistUUID(trackUUID string, limit int, page int) (TopUserResponse, error) { +func getTopUsersForArtistUUID(artistUUID string, limit int, page int) (TopUserResponse, error) { response := TopUserResponse{} - // TODO: Implement + var count int - // var count int + total, err := getDbCount( + "SELECT COUNT(*) FROM `scrobbles` "+ + "JOIN `track_artist` ON `track_artist`.`track` = `scrobbles`.`track` "+ + "WHERE `track_artist`.`artist` = UUID_TO_BIN(?, true);", artistUUID) - // total, err := getDbCount( - // "SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true) GROUP BY `track`, `user`", trackUUID) + if err != nil { + log.Printf("Failed to fetch scrobble count: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } - // if err != nil { - // log.Printf("Failed to fetch scrobble count: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } + rows, err := db.Query( + "SELECT BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username`, COUNT(*) "+ + "FROM `scrobbles` "+ + "JOIN `users` ON `scrobbles`.`user` = `users`.`uuid` "+ + "JOIN `track_artist` ON `track_artist`.`track` = `scrobbles`.`track` "+ + "WHERE `track_artist`.`artist` = UUID_TO_BIN(?, true) "+ + "GROUP BY `scrobbles`.`user` "+ + "ORDER BY COUNT(*) DESC LIMIT ?", + artistUUID, limit) - // rows, err := db.Query( - // "SELECT BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username`, COUNT(*) "+ - // "FROM `scrobbles` "+ - // "JOIN `users` ON `scrobbles`.`user` = `users`.`uuid` "+ - // "WHERE `track` = UUID_TO_BIN(?, true) "+ - // "GROUP BY `scrobbles`.`user` "+ - // "ORDER BY COUNT(*) DESC LIMIT ?", - // trackUUID, limit) + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } + defer rows.Close() - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } - // defer rows.Close() + for rows.Next() { + item := TopUserResponseItem{} + err := rows.Scan(&item.UserUUID, &item.UserName, &item.Count) + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch combined scrobbles") + } + count++ + response.Items = append(response.Items, item) + } - // for rows.Next() { - // item := TopUserResponseItem{} - // err := rows.Scan(&item.UserUUID, &item.UserName, &item.Count) - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch combined scrobbles") - // } - // count++ - // response.Items = append(response.Items, item) - // } + err = rows.Err() + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return response, errors.New("Failed to fetch scrobbles") + } - // err = rows.Err() - // if err != nil { - // log.Printf("Failed to fetch scrobbles: %+v", err) - // return response, errors.New("Failed to fetch scrobbles") - // } - - // response.Meta.Count = count - // response.Meta.Total = total - // response.Meta.Page = page + response.Meta.Count = count + response.Meta.Total = total + response.Meta.Page = page return response, nil } diff --git a/internal/goscrobble/scrobble.go b/internal/goscrobble/scrobble.go index 3ae0c844..f83858a8 100644 --- a/internal/goscrobble/scrobble.go +++ b/internal/goscrobble/scrobble.go @@ -35,6 +35,7 @@ type ScrobbleResponseItem struct { Album string `json:"album"` Track ScrobbleTrackItem `json:"track"` Source string `json:"source"` + User ScrobbleTrackItem `json:"user"` } type ScrobbleTrackItem struct { @@ -127,3 +128,49 @@ func checkIfScrobbleExists(userUuid string, timestamp time.Time, source string) return count != 0 } + +func getRecentScrobbles() (ScrobbleResponse, error) { + scrobbleReq := ScrobbleResponse{} + var count int + limit := 50 + + rows, err := db.Query( + "SELECT BIN_TO_UUID(`scrobbles`.`uuid`, true), `scrobbles`.`created_at`, BIN_TO_UUID(`artists`.`uuid`, true), `artists`.`name`, `albums`.`name`, BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, `scrobbles`.`source`, BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username` FROM `scrobbles` "+ + "JOIN tracks ON scrobbles.track = tracks.uuid "+ + "JOIN track_artist ON track_artist.track = tracks.uuid "+ + "JOIN track_album ON track_album.track = tracks.uuid "+ + "JOIN artists ON track_artist.artist = artists.uuid "+ + "JOIN albums ON track_album.album = albums.uuid "+ + "JOIN users ON scrobbles.user = users.uuid "+ + "GROUP BY scrobbles.uuid, albums.uuid "+ + "ORDER BY scrobbles.created_at DESC LIMIT ?", limit) + + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return scrobbleReq, errors.New("Failed to fetch scrobbles") + } + defer rows.Close() + + for rows.Next() { + item := ScrobbleResponseItem{} + err := rows.Scan(&item.UUID, &item.Timestamp, &item.Artist.UUID, &item.Artist.Name, &item.Album, &item.Track.UUID, &item.Track.Name, &item.Source, &item.User.UUID, &item.User.Name) + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return scrobbleReq, errors.New("Failed to fetch scrobbles") + } + count++ + scrobbleReq.Items = append(scrobbleReq.Items, item) + } + + err = rows.Err() + if err != nil { + log.Printf("Failed to fetch scrobbles: %+v", err) + return scrobbleReq, errors.New("Failed to fetch scrobbles") + } + + scrobbleReq.Meta.Count = count + scrobbleReq.Meta.Total = 50 + scrobbleReq.Meta.Page = 1 + + return scrobbleReq, nil +} diff --git a/internal/goscrobble/server.go b/internal/goscrobble/server.go index 188a45f4..9ebb0029 100644 --- a/internal/goscrobble/server.go +++ b/internal/goscrobble/server.go @@ -72,6 +72,8 @@ func HandleRequests(port string) { // No Auth v1.HandleFunc("/stats", limitMiddleware(handleStats, lightLimiter)).Methods("GET") + v1.HandleFunc("/recent", limitMiddleware(handleRecentScrobbles, lightLimiter)).Methods("GET") + v1.HandleFunc("/profile/{username}", limitMiddleware(getProfile, lightLimiter)).Methods("GET") v1.HandleFunc("/artists/top/{uuid}", limitMiddleware(getArtists, lightLimiter)).Methods("GET") @@ -844,3 +846,15 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(js) } + +func handleRecentScrobbles(w http.ResponseWriter, r *http.Request) { + scrobbleList, err := getRecentScrobbles() + if err != nil { + throwOkError(w, err.Error()) + return + } + + json, _ := json.Marshal(&scrobbleList) + w.WriteHeader(http.StatusOK) + w.Write(json) +} diff --git a/internal/goscrobble/track.go b/internal/goscrobble/track.go index 270f55d0..3e942673 100644 --- a/internal/goscrobble/track.go +++ b/internal/goscrobble/track.go @@ -302,7 +302,7 @@ func getTopUsersForTrackUUID(trackUUID string, limit int, page int) (TopUserResp var count int total, err := getDbCount( - "SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true) GROUP BY `track`, `user`", trackUUID) + "SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true)", trackUUID) if err != nil { log.Printf("Failed to fetch scrobble count: %+v", err)