/
playlist.go
155 lines (132 loc) · 3.66 KB
/
playlist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package models
import (
"log"
"math/rand"
"time"
"github.com/zmb3/spotify"
)
type Playlist struct {
ID string `json:"id"`
Name string `json:"name"`
Owner string `json:"owner"`
Endpoint string `json:"endpoint"`
TotalTracks uint `json:"total_tracks"`
}
type CachedPlaylist struct {
Playlist Playlist `json:"playlist"`
Tracks []Track `json:"tracks"`
}
func NewPlaylist(p spotify.SimplePlaylist) Playlist {
return Playlist{
ID: p.ID.String(),
Name: p.Name,
Owner: p.Owner.ID,
Endpoint: p.ExternalURLs["spotify"],
TotalTracks: p.Tracks.Total,
}
}
func NewPlaylistFromFullPlaylist(p *spotify.FullPlaylist) Playlist {
return Playlist{
ID: p.ID.String(),
Name: p.Name,
Owner: p.Owner.ID,
Endpoint: p.ExternalURLs["spotify"],
TotalTracks: uint(p.Tracks.Total),
}
}
type Playlists struct {
Playlists []Playlist
}
// QueueTracks gets this party's queue track list
func (p *Party) QueueTracks() (*[]Track, error) {
queueData, err := p.QueueG().One()
if err != nil {
return nil, err
}
var queue []Track
err = queueData.Data.Unmarshal(&queue)
if err != nil {
return nil, err
}
return &queue, nil
}
// HistoryTracks gets this party's history track list
func (p *Party) HistoryTracks() (*[]Track, error) {
historyData, err := p.HistoryG().One()
if err != nil {
return nil, err
}
var history []Track
err = historyData.Data.Unmarshal(&history)
if err != nil {
return nil, err
}
return &history, nil
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// Shuffle a playlist distributing the same artist "evenly" throughout
func Shuffle(playlist *[]Track) *[]Track {
shuffled := make([]Track, len(*playlist))
artists, longestArtistListLength := artists(*playlist)
log.Printf("There are %d artists\n", len(artists))
log.Printf("Longest artist list length: %d", longestArtistListLength)
// Shuffle: https://labs.spotify.com/2014/02/28/how-to-shuffle-songs/
// http://keyj.emphy.de/balanced-shuffle/
if longestArtistListLength > 1 {
// TODO: Fisher-Yates shuffle the tracks in the artist arrays
// TODO: Spread songs in artist arrays of length, longestArtistListLength, for each artist
// Spotify's algorithm takes into account how long a playlist is, and how many of each type
// of song there are. So if there are four White Stripes songs in the list, they will each
// appear at roughly 25% intervals.
return playlist
} else {
// Fisher-Yates shuffle the artist array
N := len(artists)
for i := 0; i < N; i++ {
// choose index uniformly in [i, N-1]
r := i + rand.Intn(N-i)
artists[r], artists[i] = artists[i], artists[r]
}
for i, tracks := range artists {
shuffled[i] = *tracks[0]
}
}
return &shuffled
}
func artists(tracks []Track) ([]([]*Track), int) {
// Split up tracks by artist, accumulating a longest list length
artists := make(map[string]([]*Track))
longestLength := 0
for _, track := range tracks {
artist := track.FirstArtist()
_, exists := artists[artist]
if exists {
artists[artist] = append(artists[artist], &track)
length := len(artists[artist])
if length > longestLength {
longestLength = length
}
}
artists[artist] = []*Track{&track}
if longestLength == 0 {
longestLength++
}
}
// Convert map to array of arrays of tracks by artist
artistsArray := make([]([]*Track), len(artists))
i := 0
for keys := range artists {
artistsArray[i] = artists[keys]
i++
}
return artistsArray, longestLength
}
// FirstArtist gets a track's first artist, or "Unknown" if no artists exist
func (t *Track) FirstArtist() string {
if len(t.Artists) == 0 {
return "Unknown"
}
return t.Artists[0].Name
}