mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-22 08:25:14 +00:00
0.0.27
- Navidrome works! - Tidy up request/response structure in backend - Tidy Settings page
This commit is contained in:
parent
48a99b31fd
commit
f4bdf3f730
@ -3,7 +3,7 @@ stages:
|
|||||||
- bundle
|
- bundle
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
VERSION: 0.0.26
|
VERSION: 0.0.27
|
||||||
|
|
||||||
build-go:
|
build-go:
|
||||||
image: golang:1.16.2
|
image: golang:1.16.2
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
# 0.0.27
|
||||||
|
- Navidrome works!
|
||||||
|
- Tidy up request/response structure in backend
|
||||||
|
- Tidy Settings page
|
||||||
|
|
||||||
# 0.0.26
|
# 0.0.26
|
||||||
- Make email required
|
- Make email required
|
||||||
- Add basic navidrome/subsonic connection
|
- Add basic navidrome/subsonic connection
|
||||||
- Tidy up request/response structure in backend
|
|
||||||
- Tidy Settings page
|
|
||||||
|
|
||||||
# 0.0.25
|
# 0.0.25
|
||||||
- Images now pull from spotify if setup!
|
- Images now pull from spotify if setup!
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package goscrobble
|
package goscrobble
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NavidromeResponse struct {
|
type NavidromeResponse struct {
|
||||||
@ -12,10 +16,41 @@ type NavidromeResponse struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Serverversion string `json:"serverVersion"`
|
ServerVersion string `json:"serverVersion"`
|
||||||
|
NowPlaying struct {
|
||||||
|
Entry []NavidromeNowPlaying `json:"entry"`
|
||||||
|
} `json:"nowPlaying"`
|
||||||
} `json:"subsonic-response"`
|
} `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
|
// updateSpotifyData - Pull data for all users
|
||||||
func updateNavidromeData() {
|
func updateNavidromeData() {
|
||||||
// Get all active users with a spotify token
|
// Get all active users with a spotify token
|
||||||
@ -26,20 +61,63 @@ func updateNavidromeData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
user.updateNavidromePlaydata()
|
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() {
|
func (user *User) updateNavidromePlaydata(tx *sql.Tx) error {
|
||||||
_, err := user.getNavidromeTokens()
|
tokens, err := user.getNavidromeTokens()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("No Navidrome token for user: %+v %+v", user.Username, err)
|
fmt.Printf("No Navidrome token for user: %+v %+v", user.Username, err)
|
||||||
return
|
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 {
|
func validateNavidromeConnection(url string, username string, hash string, salt string) error {
|
||||||
fmt.Printf("url:%s, username:%s, hash:%s, salt:%s", url, username, hash, salt)
|
|
||||||
resp, err := http.Get(url + "/rest/ping.view?u=" + username + "&t=" + hash + "&s=" + salt + "&c=GoScrobble&v=1.16.1&f=json")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -58,3 +136,51 @@ func validateNavidromeConnection(url string, username string, hash string, salt
|
|||||||
|
|
||||||
return errors.New("Failed to validate")
|
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
|
||||||
|
}
|
||||||
|
@ -733,7 +733,7 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info := ServerInfo{
|
info := ServerInfo{
|
||||||
Version: "0.0.26",
|
Version: "0.0.27",
|
||||||
RegistrationEnabled: cachedRegistrationEnabled,
|
RegistrationEnabled: cachedRegistrationEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
var endTicker chan bool
|
var endTicker chan bool
|
||||||
|
|
||||||
func StartBackgroundWorkers() {
|
func StartBackgroundWorkers() {
|
||||||
|
|
||||||
endTicker := make(chan bool)
|
endTicker := make(chan bool)
|
||||||
|
|
||||||
hourTicker := time.NewTicker(time.Duration(1) * time.Hour)
|
hourTicker := time.NewTicker(time.Duration(1) * time.Hour)
|
||||||
|
@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
const ScrobbleTable = (props) => {
|
const ScrobbleTable = (props) => {
|
||||||
return (
|
return (
|
||||||
<div style={{width: `100%`}}>
|
<div style={{width: `100%`, maxWidth: `900px`}}>
|
||||||
<table style={{width: `100%`}} border={1} cellPadding={5}>
|
<table style={{width: `100%`}} border={1} cellPadding={5}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user