mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-22 08:25:14 +00:00
0.0.14
- Add duplicate cache checker for jellyfin/multiscrobbler
This commit is contained in:
parent
ca7ef56326
commit
bda7023949
@ -3,7 +3,7 @@ stages:
|
|||||||
- bundle
|
- bundle
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
VERSION: 0.0.13
|
VERSION: 0.0.14
|
||||||
|
|
||||||
build-go:
|
build-go:
|
||||||
image: golang:1.16.2
|
image: golang:1.16.2
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
# 0.0.14
|
||||||
|
- Add duplicate cache checker for jellyfin/multiscrobbler
|
||||||
|
|
||||||
# 0.0.13
|
# 0.0.13
|
||||||
- Fix multiscrobbler support
|
- Fix multiscrobbler support
|
||||||
|
|
||||||
|
@ -9,30 +9,71 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseJellyfinInput - Transform API data into a common struct. Uses MusicBrainzID primarily
|
type JellyfinRequest struct {
|
||||||
func ParseJellyfinInput(userUUID string, data map[string]interface{}, ip net.IP, tx *sql.Tx) error {
|
Album string `json:"Album"`
|
||||||
// Debugging
|
Artist string `json:"Artist"`
|
||||||
// fmt.Printf("%+v", data)
|
ClientName string `json:"ClientName"`
|
||||||
|
DeviceID string `json:"DeviceId"`
|
||||||
|
DeviceName string `json:"DeviceName"`
|
||||||
|
IsAutomated bool `json:"IsAutomated"`
|
||||||
|
IsPaused bool `json:"IsPaused"`
|
||||||
|
ItemID string `json:"ItemId"`
|
||||||
|
ItemType string `json:"ItemType"`
|
||||||
|
MediaSourceID string `json:"MediaSourceId"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
NotificationType string `json:"NotificationType"`
|
||||||
|
Overview string `json:"Overview"`
|
||||||
|
PlaybackPosition string `json:"PlaybackPosition"`
|
||||||
|
PlaybackPositionTicks int64 `json:"PlaybackPositionTicks"`
|
||||||
|
ProviderMusicbrainzalbum string `json:"Provider_musicbrainzalbum"`
|
||||||
|
ProviderMusicbrainzalbumartist string `json:"Provider_musicbrainzalbumartist"`
|
||||||
|
ProviderMusicbrainzartist string `json:"Provider_musicbrainzartist"`
|
||||||
|
ProviderMusicbrainzreleasegroup string `json:"Provider_musicbrainzreleasegroup"`
|
||||||
|
ProviderMusicbrainztrack string `json:"Provider_musicbrainztrack"`
|
||||||
|
RunTime string `json:"RunTime"`
|
||||||
|
RunTimeTicks int64 `json:"RunTimeTicks"`
|
||||||
|
ServerID string `json:"ServerId"`
|
||||||
|
ServerName string `json:"ServerName"`
|
||||||
|
ServerURL string `json:"ServerUrl"`
|
||||||
|
ServerVersion string `json:"ServerVersion"`
|
||||||
|
Timestamp string `json:"Timestamp"`
|
||||||
|
UserID string `json:"UserId"`
|
||||||
|
Username string `json:"Username"`
|
||||||
|
UtcTimestamp string `json:"UtcTimestamp"`
|
||||||
|
Year int64 `json:"Year"`
|
||||||
|
}
|
||||||
|
|
||||||
if data["ItemType"] != "Audio" {
|
// ParseJellyfinInput - Transform API data into a common struct. Uses MusicBrainzID primarily
|
||||||
|
func ParseJellyfinInput(userUUID string, jf JellyfinRequest, ip net.IP, tx *sql.Tx) error {
|
||||||
|
// Debugging
|
||||||
|
// fmt.Printf("%+v", jf)
|
||||||
|
|
||||||
|
// Prevents scrobbling same song twice!
|
||||||
|
cacheKey := jf.UserID + ":" + jf.Name + ":" + jf.Artist + ":" + jf.Album + ":" + jf.ServerID
|
||||||
|
redisKey := getMd5(cacheKey + userUUID)
|
||||||
|
if getRedisKeyExists(redisKey) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if jf.ItemType != "Audio" {
|
||||||
return errors.New("Media type not audio")
|
return errors.New("Media type not audio")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety Checks
|
// Safety Checks
|
||||||
if data["Artist"] == nil {
|
if jf.Artist == "" {
|
||||||
return errors.New("Missing artist data")
|
return errors.New("Missing artist data")
|
||||||
}
|
}
|
||||||
|
|
||||||
if data["Album"] == nil {
|
if jf.Album == "" {
|
||||||
return errors.New("Missing album data")
|
return errors.New("Missing album data")
|
||||||
}
|
}
|
||||||
|
|
||||||
if data["Name"] == nil {
|
if jf.Name == "" {
|
||||||
return errors.New("Missing track data")
|
return errors.New("Missing track data")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert artist if not exist
|
// Insert artist if not exist
|
||||||
artist, err := insertArtist(fmt.Sprintf("%s", data["Artist"]), fmt.Sprintf("%s", data["Provider_musicbrainzartist"]), "", tx)
|
artist, err := insertArtist(jf.Artist, jf.ProviderMusicbrainzartist, "", tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%+v", err)
|
log.Printf("%+v", err)
|
||||||
return errors.New("Failed to map artist")
|
return errors.New("Failed to map artist")
|
||||||
@ -40,15 +81,15 @@ func ParseJellyfinInput(userUUID string, data map[string]interface{}, ip net.IP,
|
|||||||
|
|
||||||
// Insert album if not exist
|
// Insert album if not exist
|
||||||
artists := []string{artist.Uuid}
|
artists := []string{artist.Uuid}
|
||||||
album, err := insertAlbum(fmt.Sprintf("%s", data["Album"]), fmt.Sprintf("%s", data["Provider_musicbrainzalbum"]), "", artists, tx)
|
album, err := insertAlbum(jf.Album, jf.ProviderMusicbrainzalbum, "", artists, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%+v", err)
|
log.Printf("%+v", err)
|
||||||
return errors.New("Failed to map album")
|
return errors.New("Failed to map album")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert track if not exist
|
// Insert track if not exist
|
||||||
length := timestampToSeconds(fmt.Sprintf("%s", data["RunTime"]))
|
length := timestampToSeconds(jf.RunTime)
|
||||||
track, err := insertTrack(fmt.Sprintf("%s", data["Name"]), length, fmt.Sprintf("%s", data["Provider_musicbrainztrack"]), "", album.Uuid, artists, tx)
|
track, err := insertTrack(jf.Name, length, jf.ProviderMusicbrainztrack, "", album.Uuid, artists, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%+v", err)
|
log.Printf("%+v", err)
|
||||||
return errors.New("Failed to map track")
|
return errors.New("Failed to map track")
|
||||||
@ -63,6 +104,9 @@ func ParseJellyfinInput(userUUID string, data map[string]interface{}, ip net.IP,
|
|||||||
return errors.New("Failed to map track")
|
return errors.New("Failed to map track")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert track if not exist
|
// Add cache key!
|
||||||
|
ttl := time.Duration(timestampToSeconds(jf.RunTime)) * time.Second
|
||||||
|
setRedisValTtl(redisKey, "1", ttl)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ package goscrobble
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MultiScrobblerInput struct {
|
type MultiScrobblerRequest struct {
|
||||||
Artists []string `json:"artists"`
|
Artists []string `json:"artists"`
|
||||||
Album string `json:"album"`
|
Album string `json:"album"`
|
||||||
Track string `json:"track"`
|
Track string `json:"track"`
|
||||||
@ -17,8 +19,15 @@ type MultiScrobblerInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseMultiScrobblerInput - Transform API data
|
// ParseMultiScrobblerInput - Transform API data
|
||||||
func ParseMultiScrobblerInput(userUUID string, data MultiScrobblerInput, ip net.IP, tx *sql.Tx) error {
|
func ParseMultiScrobblerInput(userUUID string, data MultiScrobblerRequest, ip net.IP, tx *sql.Tx) error {
|
||||||
// Debugging
|
// Cache key
|
||||||
|
json, _ := json.Marshal(data)
|
||||||
|
redisKey := getMd5(string(json) + userUUID)
|
||||||
|
if getRedisKeyExists(redisKey) {
|
||||||
|
fmt.Printf("Prevented duplicate entry!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
artists := make([]string, 0)
|
artists := make([]string, 0)
|
||||||
albumartists := make([]string, 0)
|
albumartists := make([]string, 0)
|
||||||
|
|
||||||
@ -54,5 +63,9 @@ func ParseMultiScrobblerInput(userUUID string, data MultiScrobblerInput, ip net.
|
|||||||
return errors.New("Failed to map track")
|
return errors.New("Failed to map track")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add cache key for the duration of the song *2 since we're caching the start time too
|
||||||
|
ttl := time.Duration(data.Duration*2) * time.Second
|
||||||
|
setRedisValTtl(redisKey, "1", ttl)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -73,3 +73,13 @@ func getRedisVal(key string) string {
|
|||||||
|
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRedisKeyExists(key string) bool {
|
||||||
|
val, err := redisDb.Exists(ctx, redisPrefix+key).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch redis key (%+v) Error: %+v", key, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return val == 1
|
||||||
|
}
|
||||||
|
@ -228,22 +228,21 @@ func handleIngress(w http.ResponseWriter, r *http.Request, userUuid string) {
|
|||||||
|
|
||||||
switch ingressType {
|
switch ingressType {
|
||||||
case "jellyfin":
|
case "jellyfin":
|
||||||
bodyJson, err := decodeJson(r.Body)
|
jfInput := JellyfinRequest{}
|
||||||
fmt.Println(err)
|
err := json.NewDecoder(r.Body).Decode(&jfInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
throwInvalidJson(w)
|
throwInvalidJson(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ParseJellyfinInput(userUuid, bodyJson, ip, tx)
|
err = ParseJellyfinInput(userUuid, jfInput, ip, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Err? %+v", err)
|
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
throwOkError(w, err.Error())
|
throwOkError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "multiscrobbler":
|
case "multiscrobbler":
|
||||||
msInput := MultiScrobblerInput{}
|
msInput := MultiScrobblerRequest{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&msInput)
|
err := json.NewDecoder(r.Body).Decode(&msInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -448,7 +447,7 @@ func deleteSpotifyLink(w http.ResponseWriter, r *http.Request, u string, v strin
|
|||||||
|
|
||||||
func fetchServerInfo(w http.ResponseWriter, r *http.Request) {
|
func fetchServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
info := ServerInfo{
|
info := ServerInfo{
|
||||||
Version: "0.0.13",
|
Version: "0.0.14",
|
||||||
}
|
}
|
||||||
|
|
||||||
js, _ := json.Marshal(&info)
|
js, _ := json.Marshal(&info)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package goscrobble
|
package goscrobble
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -154,3 +155,8 @@ func isValidTimezone(tz string) bool {
|
|||||||
_, err := time.LoadLocation(tz)
|
_, err := time.LoadLocation(tz)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMd5(val string) string {
|
||||||
|
hash := md5.Sum([]byte(val))
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user