GoScrobble/internal/goscrobble/scrobble.go
2022-06-27 16:10:01 +12:00

176 lines
5.6 KiB
Go

package goscrobble
import (
"database/sql"
"errors"
"fmt"
"log"
"net"
"time"
)
type Scrobble struct {
Uuid string `json:"uuid"`
CreatedAt time.Time `json:"created_at"`
CreatedIp net.IP `json:"created_ip"`
User string `json:"user"`
Track string `json:"track"`
}
type ScrobbleResponse struct {
Meta ScrobbleResponseMeta `json:"meta"`
Items []ScrobbleResponseItem `json:"items"`
}
type ScrobbleResponseMeta struct {
Count int `json:"count"`
Total int `json:"total"`
Page int `json:"page"`
}
type ScrobbleResponseItem struct {
UUID string `json:"uuid"`
Timestamp time.Time `json:"time"`
Artist ScrobbleTrackItem `json:"artist"`
Album string `json:"album"`
Track ScrobbleTrackItem `json:"track"`
Source string `json:"source"`
User ScrobbleTrackItem `json:"user"`
}
type ScrobbleTrackItem struct {
UUID string `json:"uuid"`
Name string `json:"name"`
}
// insertScrobble - This will return if it exists or create it based on MBID > Name
func insertScrobble(user string, track string, source string, timestamp time.Time, ip net.IP, tx *sql.Tx) error {
err := insertNewScrobble(user, track, source, timestamp, ip, tx)
if err != nil {
log.Printf("Error inserting scrobble %s %+v", user, err)
return errors.New("Failed to insert scrobble!")
}
return nil
}
func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse, error) {
scrobbleReq := ScrobbleResponse{}
var count int
// Yeah this isn't great. But for now.. it works! Cache later
total, err := getDbCount(`SELECT COUNT(*) FROM scrobbles WHERE "user" = $1`, userUuid)
if err != nil {
log.Printf("Failed to fetch scrobble count: %+v", err)
return scrobbleReq, errors.New("Failed to fetch scrobbles")
}
rows, err := db.Query(
`SELECT scrobbles.uuid, scrobbles.created_at, artists.uuid, artists.name, albums.name, tracks.uuid, tracks.name, scrobbles.source 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 `+
`WHERE "user" = $1 `+
`GROUP BY scrobbles.uuid, albums.uuid `+
`ORDER BY scrobbles.created_at DESC LIMIT $2`,
userUuid, 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)
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 = total
scrobbleReq.Meta.Page = page
return scrobbleReq, nil
}
func insertNewScrobble(user string, track string, source string, timestamp time.Time, ip net.IP, tx *sql.Tx) error {
_, err := tx.Exec(`INSERT INTO scrobbles (uuid, created_at, created_ip, "user", track, source) `+
`VALUES ($1,$2,$3,$4,$5,$6)`, newUUID(), timestamp, ip.String(), user, track, source)
return err
}
func checkIfScrobbleExists(userUuid string, timestamp time.Time, source string) bool {
count, err := getDbCount(`SELECT COUNT(*) FROM scrobbles WHERE "user" = $1 AND created_at = $2 AND source = $3`,
userUuid, timestamp, source)
if err != nil {
fmt.Printf("Error fetching scrobble exists count: %+v", err)
return true
}
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
}