mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-22 00:21:55 +00:00
Daniel Mason
f4bdf3f730
- Navidrome works! - Tidy up request/response structure in backend - Tidy Settings page
187 lines
4.7 KiB
Go
187 lines
4.7 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)
|
|
fmt.Println(response)
|
|
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 {
|
|
// Cache key
|
|
json := fmt.Sprintf("%s:%s:%s", data.ID, data.Parent, userUUID)
|
|
redisKey := getMd5(json)
|
|
if getRedisKeyExists(redisKey) {
|
|
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")
|
|
}
|
|
|
|
// Todo: Find a better way to check dupes
|
|
ttl := time.Duration(data.Duration*2) * time.Second
|
|
setRedisValTtl(redisKey, "1", ttl)
|
|
|
|
return nil
|
|
}
|