GoScrobble/internal/goscrobble/ingress_navidrome.go
2022-01-15 19:32:29 +13:00

184 lines
4.8 KiB
Go

package goscrobble
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"net/http"
"time"
)
type NavidromeResponse struct {
Response struct {
Status string `json:"status"`
Version string `json:"version"`
Type string `json:"type"`
ServerVersion string `json:"serverVersion"`
NowPlaying struct {
Entry []NavidromeNowPlaying `json:"entry"`
} `json:"nowPlaying"`
} `json:"subsonic-response"`
}
type NavidromeNowPlaying struct {
ID string `json:"id"`
Parent string `json:"parent"`
IsDir bool `json:"isDir"`
Title string `json:"title"`
Album string `json:"album"`
Artist string `json:"artist"`
Track int `json:"track"`
Year int `json:"year"`
Genre string `json:"genre"`
CoverArt string `json:"coverArt"`
Size int `json:"size"`
ContentType string `json:"contentType"`
Suffix string `json:"suffix"`
Duration int `json:"duration"`
BitRate int `json:"bitRate"`
Path string `json:"path"`
DiscNumber int `json:"discNumber"`
Created time.Time `json:"created"`
AlbumID string `json:"albumId"`
ArtistID string `json:"artistId"`
Type string `json:"type"`
IsVideo bool `json:"isVideo"`
Username string `json:"username"`
PlayerID int `json:"playerId"`
PlayerName string `json:"playerName"`
}
// updateSpotifyData - Pull data for all users
func updateNavidromeData() {
// Get all active users with a spotify token
users, err := getAllNavidromeUsers()
if err != nil {
fmt.Printf("Failed to fetch navidrome users")
return
}
for _, user := range users {
tx, err := db.Begin()
if err != nil {
fmt.Println(err)
continue
}
err = user.updateNavidromePlaydata(tx)
if err != nil {
tx.Rollback()
fmt.Println(err)
continue
}
tx.Commit()
}
}
func (user *User) updateNavidromePlaydata(tx *sql.Tx) error {
tokens, err := user.getNavidromeTokens()
if err != nil {
fmt.Printf("No Navidrome token for user: %+v %+v", user.Username, err)
return errors.New("Failed to fetch Navidrome Tokens")
}
response, err := getNavidromeNowPlaying(&tokens)
if err != nil {
return errors.New(fmt.Sprintf("Failed to fetch Navidrome Tokens %+v", err))
}
ip := net.ParseIP("0.0.0.0")
for _, v := range response.Response.NowPlaying.Entry {
err = ParseNavidromeInput(user.UUID, v, ip, tx)
}
if err != nil {
return err
}
return nil
}
func getNavidromeNowPlaying(token *OauthToken) (NavidromeResponse, error) {
response := NavidromeResponse{}
resp, err := http.Get(token.URL + "/rest/getNowPlaying?u=" + token.Username + "&t=" + token.AccessToken + "&s=" + token.RefreshToken + "&c=GoScrobble&v=1.16.1&f=json")
if err != nil {
return response, err
}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&response)
if err != nil {
return response, err
}
return response, nil
}
func validateNavidromeConnection(url string, username string, hash string, salt string) error {
resp, err := http.Get(url + "/rest/ping.view?u=" + username + "&t=" + hash + "&s=" + salt + "&c=GoScrobble&v=1.16.1&f=json")
if err != nil {
return err
}
response := NavidromeResponse{}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&response)
if err != nil {
return err
}
if response.Response.Status == "ok" {
return nil
}
return errors.New("Failed to validate")
}
// ParseNavidromeInput - Transform API data
func ParseNavidromeInput(userUUID string, data NavidromeNowPlaying, ip net.IP, tx *sql.Tx) error {
// Custom cache key - never log the same song twice in a row for now... (:
lastPlayedTitle := getUserLastPlayed(userUUID + fmt.Sprintf(":%d", data.PlayerID))
if lastPlayedTitle == data.Title+":"+data.Album {
// If it matches last played song, skip it
return nil
}
artists := make([]string, 0)
// Insert track artists
artist, err := insertArtist(data.Artist, "", "", "", tx)
if err != nil {
log.Printf("%+v", err)
return errors.New("Failed to map artist: " + artist.Name)
}
artists = append(artists, artist.UUID)
// Insert album if not exist
album, err := insertAlbum(data.Album, "", "", artists, "", tx)
if err != nil {
log.Printf("%+v", err)
return errors.New("Failed to map album")
}
// Insert track if not exist
track, err := insertTrack(data.Title, data.Duration, "", "", album.UUID, artists, tx)
if err != nil {
log.Printf("%+v", err)
return errors.New("Failed to map track")
}
// Insert scrobble if not exist
timeNow := time.Now()
err = insertScrobble(userUUID, track.UUID, "navidrome", timeNow, ip, tx)
if err != nil {
log.Printf("%+v", err)
return errors.New("Failed to map track")
}
setUserLastPlayed(userUUID+fmt.Sprintf(":%d", data.PlayerID), data.Title+":"+data.Album)
return nil
}