-
Notifications
You must be signed in to change notification settings - Fork 4
/
browsing.go
239 lines (209 loc) · 6.48 KB
/
browsing.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
package jellyfin
import (
"encoding/json"
"fmt"
"io"
)
var (
songIncludeFields = []string{"Genres", "DateCreated", "MediaSources", "UserData", "ParentId"}
albumIncludeFields = []string{"Genres", "DateCreated", "ChildCount", "UserData", "ParentId"}
playlistIncludeFields = []string{"Genres", "DateCreated", "MediaSources", "ChildCount", "Parent", "Overview"}
artistIncludeFields = []string{"ChildCount", "UserData"}
)
// GetAlbums returns albums with given sort, filter, and paging options.
// - Can be used to get an artist's discography with ArtistID filter.
func (c *Client) GetAlbums(opts QueryOpts) ([]*Album, error) {
params := c.defaultParams()
params.enableRecursive()
params.setPaging(opts.Paging)
params.setSorting(opts.Sort)
params.setFilter(mediaTypeAlbum, opts.Filter)
params.setIncludeTypes(mediaTypeAlbum)
params.setIncludeFields(albumIncludeFields...)
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, err
}
defer resp.Close()
albums := albums{}
err = json.NewDecoder(resp).Decode(&albums)
if err != nil {
return nil, fmt.Errorf("decode json: %v", err)
}
return albums.Albums, nil
}
func (c *Client) GetAlbumArtists(opts QueryOpts) ([]*Artist, error) {
params := c.defaultParams()
params.enableRecursive()
params.setFilter(mediaTypeArtist, opts.Filter)
params.setPaging(opts.Paging)
params.setSorting(opts.Sort)
params.setIncludeTypes(mediaTypeAlbum)
params.setIncludeFields(artistIncludeFields...)
resp, err := c.get("/Artists/AlbumArtists", params)
if err != nil {
return nil, err
}
defer resp.Close()
return c.parseArtists(resp)
}
func (c *Client) GetArtist(artistID string) (*Artist, error) {
artist := &Artist{}
includeFields := append(artistIncludeFields, "Overview")
err := c.getItemByID(artistID, artist, includeFields...)
if err != nil {
return nil, err
}
return artist, nil
}
func (c *Client) GetAlbum(albumID string) (*Album, error) {
album := &Album{}
includeFields := append(albumIncludeFields, "Overview")
err := c.getItemByID(albumID, album, includeFields...)
if err != nil {
return nil, err
}
return album, nil
}
func (c *Client) GetSong(songID string) (*Song, error) {
song := &Song{}
err := c.getItemByID(songID, song, songIncludeFields...)
if err != nil {
return nil, err
}
return song, nil
}
func (c *Client) GetSimilarArtists(artistID string) ([]*Artist, error) {
params := c.defaultParams()
params.enableRecursive()
params.setIncludeTypes(mediaTypeArtist)
params.setLimit(15)
resp, err := c.get(fmt.Sprintf("/Items/%s/Similar", artistID), params)
if err != nil {
return nil, err
}
defer resp.Close()
return c.parseArtists(resp)
}
func (c *Client) GetGenres(paging Paging) ([]NameID, error) {
params := c.defaultParams()
params.enableRecursive()
params.setSorting(Sort{Field: SortByName, Mode: SortAsc})
params.setPaging(paging)
resp, err := c.get("/MusicGenres", params)
if err != nil {
return nil, err
}
defer resp.Close()
body := struct {
Items []NameID
Count int `json:"TotalRecordCount"`
}{}
err = json.NewDecoder(resp).Decode(&body)
if err != nil {
return nil, fmt.Errorf("decode json: %v", err)
}
return body.Items, nil
}
// Get songs matching the given filter criteria with given sorting and paging.
// - Can be used to get an album track list with the ParentID filter.
// - Can be used to get top songs for an artist with the ArtistId filter
// and sorting by CommunityRating descending
func (c *Client) GetSongs(opts QueryOpts) ([]*Song, error) {
params := c.defaultParams()
params.setIncludeTypes(mediaTypeAudio)
params.setPaging(opts.Paging)
params.setSorting(opts.Sort)
params.setFilter(mediaTypeAudio, opts.Filter)
params.enableRecursive()
params.setIncludeFields(songIncludeFields...)
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, err
}
defer resp.Close()
return c.parseSongs(resp)
}
// GetPlaylists retrieves all playlists. Each playlists song count is known, but songs must be
// retrieved separately
func (c *Client) GetPlaylists() ([]*Playlist, error) {
params := c.defaultParams()
params.setIncludeTypes(mediaTypePlaylist)
params.enableRecursive()
params.setIncludeFields(playlistIncludeFields...)
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, fmt.Errorf("get playlists: %v", err)
}
defer resp.Close()
dto := playlists{}
if err = json.NewDecoder(resp).Decode(&dto); err != nil {
return nil, fmt.Errorf("parse playlists: %v", err)
}
// filter to MediaType == "Audio"
musicPlaylists := make([]*Playlist, 0)
for _, pl := range dto.Playlists {
if pl.MediaType == string(mediaTypeAudio) {
musicPlaylists = append(musicPlaylists, pl)
}
}
return musicPlaylists, nil
}
func (c *Client) GetPlaylist(playlistID string) (*Playlist, error) {
playlist := &Playlist{}
includeFields := append(playlistIncludeFields, "PremiereDate", "Tags", "ProviderIds")
err := c.getItemByID(playlistID, playlist, includeFields...)
if err != nil {
return nil, err
}
return playlist, nil
}
func (c *Client) GetInstantMix(id string, idType ItemType, limit int) ([]*Song, error) {
path := "/Items/%s/InstantMix"
switch idType {
case TypeArtist:
path = "/Artists/%s/InstantMix"
case TypeAlbum:
path = "/Albums/%s/InstantMix"
case TypeSong:
path = "/Songs/%s/InstantMix"
}
params := c.defaultParams()
params.setIncludeFields(songIncludeFields...)
params.setLimit(limit)
resp, err := c.get(fmt.Sprintf(path, id), params)
if err != nil {
return nil, fmt.Errorf("get instant mix: %v", err)
}
defer resp.Close()
return c.parseSongs(resp)
}
func (c *Client) getItemByID(itemID string, dto interface{}, includeFields ...string) error {
params := c.defaultParams()
if len(includeFields) > 0 {
params.setIncludeFields(includeFields...)
}
resp, err := c.get(fmt.Sprintf("/Users/%s/Items/%s", c.userID, itemID), params)
if err != nil {
return err
}
defer resp.Close()
if err := json.NewDecoder(resp).Decode(dto); err != nil {
return fmt.Errorf("parse item: %v", err)
}
return nil
}
func (c *Client) parseArtists(resp io.Reader) ([]*Artist, error) {
artists := &artists{}
if err := json.NewDecoder(resp).Decode(&artists); err != nil {
return nil, fmt.Errorf("decode json: %v", err)
}
return artists.Artists, nil
}
func (c *Client) parseSongs(resp io.Reader) ([]*Song, error) {
songs := songs{}
if err := json.NewDecoder(resp).Decode(&songs); err != nil {
return nil, fmt.Errorf("parse songs: %v", err)
}
return songs.Songs, nil
}