- Get top tracks pulling correctly :)
This commit is contained in:
Daniel Mason 2021-04-08 19:00:13 +12:00
parent 3f3296e649
commit 07bce0ebc2
Signed by: idanoo
GPG Key ID: 387387CDBC02F132
9 changed files with 259 additions and 27 deletions

View File

@ -3,7 +3,7 @@ stages:
- bundle - bundle
variables: variables:
VERSION: 0.0.22 VERSION: 0.0.23
build-go: build-go:
image: golang:1.16.2 image: golang:1.16.2

View File

@ -1,3 +1,6 @@
# 0.0.23
- Get top tracks pulling correctly :)
# 0.0.22 # 0.0.22
- Rework navbar + user pages - Rework navbar + user pages

View File

@ -50,8 +50,11 @@ func HandleRequests(port string) {
// No Auth // No Auth
v1.HandleFunc("/stats", limitMiddleware(handleStats, lightLimiter)).Methods("GET") v1.HandleFunc("/stats", limitMiddleware(handleStats, lightLimiter)).Methods("GET")
v1.HandleFunc("/profile/{username}", limitMiddleware(getProfile, lightLimiter)).Methods("GET") v1.HandleFunc("/profile/{username}", limitMiddleware(getProfile, lightLimiter)).Methods("GET")
v1.HandleFunc("/artist/top/{uuid}", limitMiddleware(getArtists, lightLimiter)).Methods("GET")
v1.HandleFunc("/artist/{uuid}", limitMiddleware(getArtist, lightLimiter)).Methods("GET") v1.HandleFunc("/artist/{uuid}", limitMiddleware(getArtist, lightLimiter)).Methods("GET")
v1.HandleFunc("/album/top/{uuid}", limitMiddleware(getArtists, lightLimiter)).Methods("GET")
v1.HandleFunc("/album/{uuid}", limitMiddleware(getAlbum, lightLimiter)).Methods("GET") v1.HandleFunc("/album/{uuid}", limitMiddleware(getAlbum, lightLimiter)).Methods("GET")
v1.HandleFunc("/track/top/{uuid}", limitMiddleware(getTracks, lightLimiter)).Methods("GET")
v1.HandleFunc("/track/{uuid}", limitMiddleware(getTrack, lightLimiter)).Methods("GET") v1.HandleFunc("/track/{uuid}", limitMiddleware(getTrack, lightLimiter)).Methods("GET")
v1.HandleFunc("/register", limitMiddleware(handleRegister, heavyLimiter)).Methods("POST") v1.HandleFunc("/register", limitMiddleware(handleRegister, heavyLimiter)).Methods("POST")
@ -533,6 +536,81 @@ func getTrack(w http.ResponseWriter, r *http.Request) {
w.Write(json) w.Write(json)
} }
// getArtists - Returns artist data for a user
func getArtists(w http.ResponseWriter, r *http.Request) {
var uuid string
for k, v := range mux.Vars(r) {
if k == "uuid" {
uuid = v
}
}
if uuid == "" {
throwOkError(w, "Invalid UUID")
return
}
artist, err := getArtistByUUID(uuid)
if err != nil {
throwOkError(w, err.Error())
return
}
json, _ := json.Marshal(&artist)
w.WriteHeader(http.StatusOK)
w.Write(json)
}
// getAlbums - Returns album data for a user
func getAlbums(w http.ResponseWriter, r *http.Request) {
var uuid string
for k, v := range mux.Vars(r) {
if k == "uuid" {
uuid = v
}
}
if uuid == "" {
throwOkError(w, "Invalid UUID")
return
}
album, err := getAlbumByUUID(uuid)
if err != nil {
throwOkError(w, err.Error())
return
}
json, _ := json.Marshal(&album)
w.WriteHeader(http.StatusOK)
w.Write(json)
}
// getTracks - Returns track data for a user
func getTracks(w http.ResponseWriter, r *http.Request) {
var uuid string
for k, v := range mux.Vars(r) {
if k == "uuid" {
uuid = v
}
}
if uuid == "" {
throwOkError(w, "Invalid UUID")
return
}
track, err := getTopTracks(uuid)
if err != nil {
throwOkError(w, err.Error())
return
}
json, _ := json.Marshal(&track)
w.WriteHeader(http.StatusOK)
w.Write(json)
}
// postSpotifyResponse - Oauth Response from Spotify // postSpotifyResponse - Oauth Response from Spotify
func postSpotifyReponse(w http.ResponseWriter, r *http.Request) { func postSpotifyReponse(w http.ResponseWriter, r *http.Request) {
err := connectSpotifyResponse(r) err := connectSpotifyResponse(r)
@ -587,7 +665,7 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) {
} }
info := ServerInfo{ info := ServerInfo{
Version: "0.0.22", Version: "0.0.23",
RegistrationEnabled: cachedRegistrationEnabled, RegistrationEnabled: cachedRegistrationEnabled,
} }

View File

@ -17,6 +17,16 @@ type Track struct {
SpotifyID string `json:"spotify_id"` SpotifyID string `json:"spotify_id"`
} }
type TopTrack struct {
UUID string `json:"uuid"`
Name string `json:"name"`
Img string `json:"img"`
Plays int `json:"plays"`
}
type TopTracks struct {
Tracks map[int]TopTrack `json:"tracks"`
}
// insertTrack - This will return if it exists or create it based on MBID > Name // insertTrack - This will return if it exists or create it based on MBID > Name
func insertTrack(name string, legnth int, mbid string, spotifyId string, album string, artists []string, tx *sql.Tx) (Track, error) { func insertTrack(name string, legnth int, mbid string, spotifyId string, album string, artists []string, tx *sql.Tx) (Track, error) {
track := Track{} track := Track{}
@ -164,3 +174,42 @@ func getTrackByUUID(uuid string) (Track, error) {
return track, nil return track, nil
} }
func getTopTracks(userUuid string) (TopTracks, error) {
var topTracks TopTracks
rows, err := db.Query("SELECT BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, IFNULL(`artists`.`img`,''), count(*) "+
"FROM `scrobbles` "+
"JOIN `tracks` ON `tracks`.`uuid` = `scrobbles`.`track` "+
"JOIN track_artist ON track_artist.track = tracks.uuid "+
"JOIN artists ON track_artist.artist = artists.uuid "+
"WHERE `user` = UUID_TO_BIN(?, true) "+
"GROUP BY `scrobbles`.`track` "+
"ORDER BY count(*) DESC "+
"LIMIT 14",
userUuid)
if err != nil {
log.Printf("Failed to fetch top tracks: %+v", err)
return topTracks, errors.New("Failed to fetch top tracks")
}
defer rows.Close()
i := 1
tempTracks := make(map[int]TopTrack)
for rows.Next() {
var track TopTrack
err := rows.Scan(&track.UUID, &track.Name, &track.Img, &track.Plays)
if err != nil {
log.Printf("Failed to fetch track: %+v", err)
return topTracks, errors.New("Failed to fetch track")
}
tempTracks[i] = track
i++
}
topTracks.Tracks = tempTracks
return topTracks, nil
}

View File

@ -317,3 +317,12 @@ export const getTrack = (uuid) => {
return handleErrorResp(error) return handleErrorResp(error)
}); });
}; };
export const getTopTracks = (uuid) => {
return axios.get(process.env.REACT_APP_API_URL + "track/top/" + uuid).then(
(data) => {
return data.data;
}).catch((error) => {
return handleErrorResp(error)
});
}

View File

@ -3,41 +3,121 @@ import './TopTable.css'
import TopTableBox from './TopTableBox'; import TopTableBox from './TopTableBox';
const TopTable = (props) => { const TopTable = (props) => {
if (!props.items || !props.items.tracks) {
return (
<span>No data.</span>
)
}
let tracks = props.items.tracks;
return ( return (
<div> <div>
<span>Top {props.type}</span> <span>Top {props.type}s</span>
<div className="biggestWrapper"> <div className="biggestWrapper">
<div className="biggestBox"> <div className="biggestBox">
<TopTableBox <TopTableBox
size={300} size={300}
number="1" number="1"
title="hot milk" title={tracks[1].name}
uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" link={"/" + props.type + "/" + tracks[1].uuid}
img="https://i.scdn.co/image/a397625e38fb671f1baa81997b4c1fd2670fcb10" img={tracks[1].img}
/> />
</div> </div>
<div className="biggestBox"> <div className="biggestBox">
<TopTableBox <TopTableBox
size={150} size={150}
number="2" number="2"
title="Pendulum" title={tracks[2].name}
uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" link={"/" + props.type + "/" + tracks[2].uuid}
img="https://i.scdn.co/image/0f476171f283207656e95e1005cea7040be475d7" img={tracks[2].img}
/>
<TopTableBox
size={150}
number="3"
title={tracks[3].name}
link={"/" + props.type + "/" + tracks[3].uuid}
img={tracks[3].img}
/>
<TopTableBox
size={150}
number="4"
title={tracks[4].name}
link={"/" + props.type + "/" + tracks[4].uuid}
img={tracks[4].img}
/>
<TopTableBox
size={150}
number="5"
title={tracks[5].name}
link={"/" + props.type + "/" + tracks[5].uuid}
img={tracks[5].img}
/> />
<TopTableBox size={150} number="3" title="Illenium" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" />
<TopTableBox size={150} number="4" title="As It is" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" />
<TopTableBox size={150} number="5" title="CHVRCHES" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" />
</div> </div>
<div className="biggestBox"> <div className="biggestBox">
<TopTableBox size={100} number="6" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> <TopTableBox
<TopTableBox size={100} number="7" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> size={100}
<TopTableBox size={100} number="8" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> number="6"
<TopTableBox size={100} number="9" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> title={tracks[6].name}
<TopTableBox size={100} number="10" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> link={"/" + props.type + "/" + tracks[6].uuid}
<TopTableBox size={100} number="11" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> img={tracks[6].img}
<TopTableBox size={100} number="12" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> />
<TopTableBox size={100} number="13" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> <TopTableBox
<TopTableBox size={100} number="14" title="tester" uuid="a2bcc230-f7be-4087-b49a-8c43d19ed316" /> size={100}
number="7"
title={tracks[7].name}
link={"/" + props.type + "/" + tracks[7].uuid}
img={tracks[7].img}
/>
<TopTableBox
size={100}
number="8"
title={tracks[8].name}
link={"/" + props.type + "/" + tracks[8].uuid}
img={tracks[8].img}
/>
<TopTableBox
size={100}
number="9"
title={tracks[9].name}
link={"/" + props.type + "/" + tracks[9].uuid}
img={tracks[9].img}
/>
<TopTableBox
size={100}
number="10"
title={tracks[10].name}
link={"/" + props.type + "/" + tracks[10].uuid}
img={tracks[10].img}
/>
<TopTableBox
size={100}
number="11"
title={tracks[11].name}
link={"/" + props.type + "/" + tracks[11].uuid}
img={tracks[11].img}
/>
<TopTableBox
size={100}
number="12"
title={tracks[12].name}
link={"/" + props.type + "/" + tracks[12].uuid}
img={tracks[12].img}
/>
<TopTableBox
size={100}
number="13"
title={tracks[13].name}
link={"/" + props.type + "/" + tracks[13].uuid}
img={tracks[13].img}
/>
<TopTableBox
size={100}
number="14"
title={tracks[14].name}
link={"/" + props.type + "/" + tracks[14].uuid}
img={tracks[14].img}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,8 +11,9 @@ img {
.topOverlay { .topOverlay {
position: absolute; position: absolute;
margin: 5px; margin: 5px;
padding: 0 5px 0 5px; padding: 2px 5px 5px 5px;
background-color: rgba(0, 0, 0, 0.6); background-color: rgba(0, 0, 0, 0.6);
line-height: 0.6em;
} }
.topText { .topText {

View File

@ -9,7 +9,7 @@ const TopTableBox = (props) => {
} }
return ( return (
<Link to={"/artist/"+props.uuid} float="left" > <Link to={props.link} float="left" >
<div <div
className="topTableBox" className="topTableBox"
style={{ style={{
@ -20,8 +20,10 @@ const TopTableBox = (props) => {
height: `${props.size}px`, height: `${props.size}px`,
float: `left`, float: `left`,
}} > }} >
<div className="topOverlay"> <div className="topOverlay" style={{ maxWidth: `${props.size-'10'}px` }}>
<span className="topText">#{props.number} {props.title}</span> <span className="topText" style={{
fontSize: `${props.size === 300 ? '11pt' : (props.size === 150 ? '8pt': '8pt' )}`
}}>#{props.number} {props.title}</span>
</div> </div>
</div> </div>
</Link> </Link>

View File

@ -2,13 +2,14 @@ import React, { useState, useEffect } from 'react';
import '../App.css'; import '../App.css';
import './Profile.css'; import './Profile.css';
import ScaleLoader from 'react-spinners/ScaleLoader'; import ScaleLoader from 'react-spinners/ScaleLoader';
import { getProfile } from '../Api/index' import { getProfile, getTopTracks } from '../Api/index'
import ScrobbleTable from '../Components/ScrobbleTable' import ScrobbleTable from '../Components/ScrobbleTable'
import TopTable from '../Components/TopTable' import TopTable from '../Components/TopTable'
const Profile = (route) => { const Profile = (route) => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [profile, setProfile] = useState({}); const [profile, setProfile] = useState({});
const [topTracks, setTopTracks] = useState({})
let username = false; let username = false;
if (route && route.match && route.match.params && route.match.params.uuid) { if (route && route.match && route.match.params && route.match.params.uuid) {
@ -25,8 +26,17 @@ const Profile = (route) => {
getProfile(username) getProfile(username)
.then(data => { .then(data => {
setProfile(data); setProfile(data);
// Fetch top tracks
getTopTracks(data.uuid)
.then(data => {
setTopTracks(data)
}
)
setLoading(false); setLoading(false);
}) })
}, [username]) }, [username])
if (loading) { if (loading) {
@ -51,7 +61,7 @@ const Profile = (route) => {
{profile.username}'s Profile {profile.username}'s Profile
</h1> </h1>
<div className="pageBody"> <div className="pageBody">
<TopTable type="Artists" /> <TopTable type="track" items={topTracks} />
<br/> <br/>
Last 10 scrobbles...<br/> Last 10 scrobbles...<br/>
<ScrobbleTable data={profile.scrobbles}/> <ScrobbleTable data={profile.scrobbles}/>