rockbox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +473 -0
- data/lib/rockbox/api/bluetooth.rb +52 -0
- data/lib/rockbox/api/browse.rb +31 -0
- data/lib/rockbox/api/devices.rb +38 -0
- data/lib/rockbox/api/library.rb +167 -0
- data/lib/rockbox/api/playback.rb +148 -0
- data/lib/rockbox/api/playlist.rb +127 -0
- data/lib/rockbox/api/saved_playlists.rb +163 -0
- data/lib/rockbox/api/settings.rb +118 -0
- data/lib/rockbox/api/smart_playlists.rb +115 -0
- data/lib/rockbox/api/sound.rb +31 -0
- data/lib/rockbox/api/system.rb +32 -0
- data/lib/rockbox/case_conversion.rb +43 -0
- data/lib/rockbox/client.rb +226 -0
- data/lib/rockbox/configuration.rb +38 -0
- data/lib/rockbox/errors.rb +33 -0
- data/lib/rockbox/events.rb +78 -0
- data/lib/rockbox/plugin.rb +60 -0
- data/lib/rockbox/transport.rb +202 -0
- data/lib/rockbox/types.rb +152 -0
- data/lib/rockbox/version.rb +5 -0
- data/lib/rockbox.rb +19 -0
- data/rockbox.gemspec +36 -0
- metadata +124 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../types"
|
|
4
|
+
|
|
5
|
+
module Rockbox
|
|
6
|
+
module Api
|
|
7
|
+
class Library
|
|
8
|
+
TRACK_FIELDS = <<~GQL
|
|
9
|
+
fragment TrackFields on Track {
|
|
10
|
+
id title artist album genre disc trackString yearString
|
|
11
|
+
composer comment albumArtist grouping
|
|
12
|
+
discnum tracknum layer year bitrate frequency
|
|
13
|
+
filesize length elapsed path
|
|
14
|
+
albumId artistId genreId albumArt
|
|
15
|
+
}
|
|
16
|
+
GQL
|
|
17
|
+
|
|
18
|
+
ALBUM_FIELDS = <<~GQL
|
|
19
|
+
fragment AlbumFields on Album {
|
|
20
|
+
id title artist year yearString albumArt md5 artistId copyrightMessage
|
|
21
|
+
}
|
|
22
|
+
GQL
|
|
23
|
+
|
|
24
|
+
ARTIST_FIELDS = <<~GQL
|
|
25
|
+
fragment ArtistFields on Artist { id name bio image }
|
|
26
|
+
GQL
|
|
27
|
+
|
|
28
|
+
def initialize(http)
|
|
29
|
+
@http = http
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------
|
|
33
|
+
# Albums
|
|
34
|
+
# ---------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
def albums
|
|
37
|
+
data = @http.execute(<<~GQL)
|
|
38
|
+
#{ALBUM_FIELDS}
|
|
39
|
+
query Albums { albums { ...AlbumFields tracks { id title path length albumArt } } }
|
|
40
|
+
GQL
|
|
41
|
+
Array(data[:albums]).map { |a| Album.from_hash(a) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def album(id)
|
|
45
|
+
data = @http.execute(<<~GQL, { id: id })
|
|
46
|
+
#{TRACK_FIELDS}
|
|
47
|
+
#{ALBUM_FIELDS}
|
|
48
|
+
query Album($id: String!) { album(id: $id) { ...AlbumFields tracks { ...TrackFields } } }
|
|
49
|
+
GQL
|
|
50
|
+
Album.from_hash(data[:album])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def liked_albums
|
|
54
|
+
data = @http.execute("#{ALBUM_FIELDS}\nquery LikedAlbums { likedAlbums { ...AlbumFields } }")
|
|
55
|
+
Array(data[:liked_albums]).map { |a| Album.from_hash(a) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def like_album(id)
|
|
59
|
+
@http.execute("mutation LikeAlbum($id: String!) { likeAlbum(id: $id) }", { id: id })
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def unlike_album(id)
|
|
64
|
+
@http.execute("mutation UnlikeAlbum($id: String!) { unlikeAlbum(id: $id) }", { id: id })
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------
|
|
69
|
+
# Artists
|
|
70
|
+
# ---------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
def artists
|
|
73
|
+
data = @http.execute(<<~GQL)
|
|
74
|
+
#{ARTIST_FIELDS}
|
|
75
|
+
query Artists { artists { ...ArtistFields albums { id title albumArt year } } }
|
|
76
|
+
GQL
|
|
77
|
+
Array(data[:artists]).map { |a| Artist.from_hash(a) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def artist(id)
|
|
81
|
+
data = @http.execute(<<~GQL, { id: id })
|
|
82
|
+
#{ARTIST_FIELDS}
|
|
83
|
+
#{TRACK_FIELDS}
|
|
84
|
+
query Artist($id: String!) {
|
|
85
|
+
artist(id: $id) {
|
|
86
|
+
...ArtistFields
|
|
87
|
+
albums { id title albumArt year yearString md5 artistId tracks { id title path length } }
|
|
88
|
+
tracks { ...TrackFields }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
GQL
|
|
92
|
+
Artist.from_hash(data[:artist])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------
|
|
96
|
+
# Tracks
|
|
97
|
+
# ---------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
def tracks
|
|
100
|
+
data = @http.execute("#{TRACK_FIELDS}\nquery Tracks { tracks { ...TrackFields } }")
|
|
101
|
+
Array(data[:tracks]).map { |t| Track.from_hash(t) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def track(id)
|
|
105
|
+
data = @http.execute(
|
|
106
|
+
"#{TRACK_FIELDS}\nquery Track($id: String!) { track(id: $id) { ...TrackFields } }",
|
|
107
|
+
{ id: id }
|
|
108
|
+
)
|
|
109
|
+
Track.from_hash(data[:track])
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def liked_tracks
|
|
113
|
+
data = @http.execute("#{TRACK_FIELDS}\nquery LikedTracks { likedTracks { ...TrackFields } }")
|
|
114
|
+
Array(data[:liked_tracks]).map { |t| Track.from_hash(t) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def like_track(id)
|
|
118
|
+
@http.execute("mutation LikeTrack($id: String!) { likeTrack(id: $id) }", { id: id })
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def unlike_track(id)
|
|
123
|
+
@http.execute("mutation UnlikeTrack($id: String!) { unlikeTrack(id: $id) }", { id: id })
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# ---------------------------------------------------------------------
|
|
128
|
+
# Search
|
|
129
|
+
# ---------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
# @return [Rockbox::SearchResults]
|
|
132
|
+
def search(term)
|
|
133
|
+
data = @http.execute(<<~GQL, { term: term })
|
|
134
|
+
#{TRACK_FIELDS}
|
|
135
|
+
#{ALBUM_FIELDS}
|
|
136
|
+
#{ARTIST_FIELDS}
|
|
137
|
+
query Search($term: String!) {
|
|
138
|
+
search(term: $term) {
|
|
139
|
+
artists { ...ArtistFields }
|
|
140
|
+
albums { ...AlbumFields }
|
|
141
|
+
tracks { ...TrackFields }
|
|
142
|
+
likedTracks { ...TrackFields }
|
|
143
|
+
likedAlbums { ...AlbumFields }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
GQL
|
|
147
|
+
results = data[:search] || {}
|
|
148
|
+
SearchResults.new(
|
|
149
|
+
artists: Array(results[:artists]).map { |a| Artist.from_hash(a) },
|
|
150
|
+
albums: Array(results[:albums]).map { |a| Album.from_hash(a) },
|
|
151
|
+
tracks: Array(results[:tracks]).map { |t| Track.from_hash(t) },
|
|
152
|
+
liked_tracks: Array(results[:liked_tracks]).map { |t| Track.from_hash(t) },
|
|
153
|
+
liked_albums: Array(results[:liked_albums]).map { |a| Album.from_hash(a) }
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------------------------------------
|
|
158
|
+
# Library management
|
|
159
|
+
# ---------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
def scan
|
|
162
|
+
@http.execute("mutation ScanLibrary { scanLibrary }")
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../types"
|
|
4
|
+
|
|
5
|
+
module Rockbox
|
|
6
|
+
module Api
|
|
7
|
+
class Playback
|
|
8
|
+
TRACK_FIELDS = <<~GQL
|
|
9
|
+
fragment TrackFields on Track {
|
|
10
|
+
id title artist album genre disc trackString yearString
|
|
11
|
+
composer comment albumArtist grouping
|
|
12
|
+
discnum tracknum layer year bitrate frequency
|
|
13
|
+
filesize length elapsed path
|
|
14
|
+
albumId artistId genreId albumArt
|
|
15
|
+
}
|
|
16
|
+
GQL
|
|
17
|
+
|
|
18
|
+
def initialize(http)
|
|
19
|
+
@http = http
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------
|
|
23
|
+
# Status & current track
|
|
24
|
+
# ---------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
# @return [Integer] one of {Rockbox::PlaybackStatus} constants.
|
|
27
|
+
def status
|
|
28
|
+
@http.execute("query PlaybackStatus { status }")[:status]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Symbol] :stopped | :playing | :paused | :unknown
|
|
32
|
+
def status_name
|
|
33
|
+
PlaybackStatus.name(status)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Rockbox::Track, nil]
|
|
37
|
+
def current_track
|
|
38
|
+
data = @http.execute("#{TRACK_FIELDS}\nquery CurrentTrack { currentTrack { ...TrackFields } }")
|
|
39
|
+
Track.from_hash(data[:current_track])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Rockbox::Track, nil]
|
|
43
|
+
def next_track
|
|
44
|
+
data = @http.execute("#{TRACK_FIELDS}\nquery NextTrack { nextTrack { ...TrackFields } }")
|
|
45
|
+
Track.from_hash(data[:next_track])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Integer]
|
|
49
|
+
def file_position
|
|
50
|
+
@http.execute("query FilePosition { getFilePosition }")[:get_file_position]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------
|
|
54
|
+
# Transport controls
|
|
55
|
+
# ---------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
def play(elapsed: 0, offset: 0)
|
|
58
|
+
@http.execute(
|
|
59
|
+
"mutation Play($elapsed: Long!, $offset: Long!) { play(elapsed: $elapsed, offset: $offset) }",
|
|
60
|
+
{ elapsed: elapsed, offset: offset }
|
|
61
|
+
)
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def pause; @http.execute("mutation Pause { pause }"); nil; end
|
|
66
|
+
def resume; @http.execute("mutation Resume { resume }"); nil; end
|
|
67
|
+
def next!; @http.execute("mutation Next { next }"); nil; end
|
|
68
|
+
def previous!; @http.execute("mutation Previous { previous }"); nil; end
|
|
69
|
+
def stop; @http.execute("mutation Stop { hardStop }"); nil; end
|
|
70
|
+
def flush_and_reload; @http.execute("mutation FlushReload { flushAndReloadTracks }"); nil; end
|
|
71
|
+
|
|
72
|
+
# @param position_ms [Integer] absolute target position, in milliseconds.
|
|
73
|
+
def seek(position_ms)
|
|
74
|
+
@http.execute(
|
|
75
|
+
"mutation Seek($newTime: Int!) { fastForwardRewind(newTime: $newTime) }",
|
|
76
|
+
{ new_time: position_ms }
|
|
77
|
+
)
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------
|
|
82
|
+
# Single-call play helpers
|
|
83
|
+
# ---------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
def play_track(path)
|
|
86
|
+
@http.execute(
|
|
87
|
+
"mutation PlayTrack($path: String!) { playTrack(path: $path) }",
|
|
88
|
+
{ path: path }
|
|
89
|
+
)
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def play_album(album_id, shuffle: nil, position: nil)
|
|
94
|
+
@http.execute(
|
|
95
|
+
"mutation PlayAlbum($albumId: String!, $shuffle: Boolean, $position: Int) { " \
|
|
96
|
+
"playAlbum(albumId: $albumId, shuffle: $shuffle, position: $position) }",
|
|
97
|
+
{ album_id: album_id, shuffle: shuffle, position: position }.compact
|
|
98
|
+
)
|
|
99
|
+
nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def play_artist(artist_id, shuffle: nil, position: nil)
|
|
103
|
+
@http.execute(
|
|
104
|
+
"mutation PlayArtist($artistId: String!, $shuffle: Boolean, $position: Int) { " \
|
|
105
|
+
"playArtistTracks(artistId: $artistId, shuffle: $shuffle, position: $position) }",
|
|
106
|
+
{ artist_id: artist_id, shuffle: shuffle, position: position }.compact
|
|
107
|
+
)
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def play_playlist(playlist_id, shuffle: nil, position: nil)
|
|
112
|
+
@http.execute(
|
|
113
|
+
"mutation PlayPlaylist($playlistId: String!, $shuffle: Boolean, $position: Int) { " \
|
|
114
|
+
"playPlaylist(playlistId: $playlistId, shuffle: $shuffle, position: $position) }",
|
|
115
|
+
{ playlist_id: playlist_id, shuffle: shuffle, position: position }.compact
|
|
116
|
+
)
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def play_directory(path, recurse: nil, shuffle: nil, position: nil)
|
|
121
|
+
@http.execute(
|
|
122
|
+
"mutation PlayDirectory($path: String!, $recurse: Boolean, $shuffle: Boolean, $position: Int) { " \
|
|
123
|
+
"playDirectory(path: $path, recurse: $recurse, shuffle: $shuffle, position: $position) }",
|
|
124
|
+
{ path: path, recurse: recurse, shuffle: shuffle, position: position }.compact
|
|
125
|
+
)
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def play_liked_tracks(shuffle: nil, position: nil)
|
|
130
|
+
@http.execute(
|
|
131
|
+
"mutation PlayLikedTracks($shuffle: Boolean, $position: Int) { " \
|
|
132
|
+
"playLikedTracks(shuffle: $shuffle, position: $position) }",
|
|
133
|
+
{ shuffle: shuffle, position: position }.compact
|
|
134
|
+
)
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def play_all_tracks(shuffle: nil, position: nil)
|
|
139
|
+
@http.execute(
|
|
140
|
+
"mutation PlayAllTracks($shuffle: Boolean, $position: Int) { " \
|
|
141
|
+
"playAllTracks(shuffle: $shuffle, position: $position) }",
|
|
142
|
+
{ shuffle: shuffle, position: position }.compact
|
|
143
|
+
)
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../types"
|
|
4
|
+
|
|
5
|
+
module Rockbox
|
|
6
|
+
module Api
|
|
7
|
+
class Playlist
|
|
8
|
+
TRACK_FIELDS = <<~GQL
|
|
9
|
+
fragment TrackFields on Track {
|
|
10
|
+
id title artist album genre disc trackString yearString
|
|
11
|
+
composer comment albumArtist grouping
|
|
12
|
+
discnum tracknum layer year bitrate frequency
|
|
13
|
+
filesize length elapsed path
|
|
14
|
+
albumId artistId genreId albumArt
|
|
15
|
+
}
|
|
16
|
+
GQL
|
|
17
|
+
|
|
18
|
+
def initialize(http)
|
|
19
|
+
@http = http
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Rockbox::Playlist]
|
|
23
|
+
def current
|
|
24
|
+
data = @http.execute(<<~GQL)
|
|
25
|
+
#{TRACK_FIELDS}
|
|
26
|
+
query CurrentPlaylist {
|
|
27
|
+
playlistGetCurrent {
|
|
28
|
+
amount index maxPlaylistSize firstIndex
|
|
29
|
+
lastInsertPos seed lastShuffledStart
|
|
30
|
+
tracks { ...TrackFields }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
GQL
|
|
34
|
+
pl = data[:playlist_get_current] || {}
|
|
35
|
+
Rockbox::Playlist.new(
|
|
36
|
+
amount: pl[:amount],
|
|
37
|
+
index: pl[:index],
|
|
38
|
+
max_playlist_size: pl[:max_playlist_size],
|
|
39
|
+
first_index: pl[:first_index],
|
|
40
|
+
last_insert_pos: pl[:last_insert_pos],
|
|
41
|
+
seed: pl[:seed],
|
|
42
|
+
last_shuffled_start: pl[:last_shuffled_start],
|
|
43
|
+
tracks: Array(pl[:tracks]).map { |t| Track.from_hash(t) }
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def amount
|
|
48
|
+
@http.execute("query PlaylistAmount { playlistAmount }")[:playlist_amount]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------
|
|
52
|
+
# Queue management
|
|
53
|
+
# ---------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
# @param paths [Array<String>] file paths or track IDs to insert.
|
|
56
|
+
# @param position [Integer] one of {Rockbox::InsertPosition} (default: NEXT).
|
|
57
|
+
# @param playlist_id [String, nil] target playlist; nil for the active queue.
|
|
58
|
+
def insert_tracks(paths, position: InsertPosition::NEXT, playlist_id: nil)
|
|
59
|
+
@http.execute(
|
|
60
|
+
"mutation InsertTracks($playlistId: String, $position: Int!, $tracks: [String!]!) { " \
|
|
61
|
+
"insertTracks(playlistId: $playlistId, position: $position, tracks: $tracks) }",
|
|
62
|
+
{ playlist_id: playlist_id, position: position, tracks: paths }
|
|
63
|
+
)
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def insert_directory(directory, position: InsertPosition::LAST, playlist_id: nil)
|
|
68
|
+
@http.execute(
|
|
69
|
+
"mutation InsertDirectory($playlistId: String, $position: Int!, $directory: String!) { " \
|
|
70
|
+
"insertDirectory(playlistId: $playlistId, position: $position, directory: $directory) }",
|
|
71
|
+
{ playlist_id: playlist_id, position: position, directory: directory }
|
|
72
|
+
)
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def insert_album(album_id, position: InsertPosition::LAST)
|
|
77
|
+
@http.execute(
|
|
78
|
+
"mutation InsertAlbum($albumId: String!, $position: Int!) { " \
|
|
79
|
+
"insertAlbum(albumId: $albumId, position: $position) }",
|
|
80
|
+
{ album_id: album_id, position: position }
|
|
81
|
+
)
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def remove_track(index)
|
|
86
|
+
@http.execute(
|
|
87
|
+
"mutation RemoveTrack($index: Int!) { playlistRemoveTrack(index: $index) }",
|
|
88
|
+
{ index: index }
|
|
89
|
+
)
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def clear
|
|
94
|
+
@http.execute("mutation ClearPlaylist { playlistRemoveAllTracks }")
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def shuffle
|
|
99
|
+
@http.execute("mutation ShufflePlaylist { shufflePlaylist }")
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def create(name, tracks)
|
|
104
|
+
@http.execute(
|
|
105
|
+
"mutation CreatePlaylist($name: String!, $tracks: [String!]!) { " \
|
|
106
|
+
"playlistCreate(name: $name, tracks: $tracks) }",
|
|
107
|
+
{ name: name, tracks: tracks }
|
|
108
|
+
)
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def start(start_index: nil, elapsed: nil, offset: nil)
|
|
113
|
+
@http.execute(
|
|
114
|
+
"mutation PlaylistStart($startIndex: Int, $elapsed: Int, $offset: Int) { " \
|
|
115
|
+
"playlistStart(startIndex: $startIndex, elapsed: $elapsed, offset: $offset) }",
|
|
116
|
+
{ start_index: start_index, elapsed: elapsed, offset: offset }.compact
|
|
117
|
+
)
|
|
118
|
+
nil
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def resume
|
|
122
|
+
@http.execute("mutation PlaylistResume { playlistResume }")
|
|
123
|
+
nil
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../types"
|
|
4
|
+
|
|
5
|
+
module Rockbox
|
|
6
|
+
module Api
|
|
7
|
+
class SavedPlaylists
|
|
8
|
+
def initialize(http)
|
|
9
|
+
@http = http
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def list(folder_id: nil)
|
|
13
|
+
data = @http.execute(
|
|
14
|
+
"query SavedPlaylists($folderId: String) { " \
|
|
15
|
+
"savedPlaylists(folderId: $folderId) { id name description image folderId trackCount createdAt updatedAt } }",
|
|
16
|
+
{ folder_id: folder_id }.compact
|
|
17
|
+
)
|
|
18
|
+
Array(data[:saved_playlists]).map { |p| SavedPlaylist.from_hash(p) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get(id)
|
|
22
|
+
data = @http.execute(
|
|
23
|
+
"query SavedPlaylist($id: String!) { " \
|
|
24
|
+
"savedPlaylist(id: $id) { id name description image folderId trackCount createdAt updatedAt } }",
|
|
25
|
+
{ id: id }
|
|
26
|
+
)
|
|
27
|
+
SavedPlaylist.from_hash(data[:saved_playlist])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def track_ids(playlist_id)
|
|
31
|
+
@http.execute(
|
|
32
|
+
"query SavedPlaylistTrackIds($playlistId: String!) { savedPlaylistTrackIds(playlistId: $playlistId) }",
|
|
33
|
+
{ playlist_id: playlist_id }
|
|
34
|
+
)[:saved_playlist_track_ids] || []
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @example Builder-friendly
|
|
38
|
+
# client.saved_playlists.create(name: "Late nights") do |p|
|
|
39
|
+
# p.description = "After-dark vibes"
|
|
40
|
+
# p.track_ids = ["abc", "def"]
|
|
41
|
+
# end
|
|
42
|
+
def create(name:, description: nil, image: nil, folder_id: nil, track_ids: nil)
|
|
43
|
+
builder = CreateBuilder.new(name, description, image, folder_id, track_ids)
|
|
44
|
+
yield builder if block_given?
|
|
45
|
+
|
|
46
|
+
data = @http.execute(<<~GQL, builder.to_variables)
|
|
47
|
+
mutation CreateSavedPlaylist(
|
|
48
|
+
$name: String!, $description: String, $image: String,
|
|
49
|
+
$folderId: String, $trackIds: [String!]
|
|
50
|
+
) {
|
|
51
|
+
createSavedPlaylist(
|
|
52
|
+
name: $name, description: $description, image: $image,
|
|
53
|
+
folderId: $folderId, trackIds: $trackIds
|
|
54
|
+
) {
|
|
55
|
+
id name description image folderId trackCount createdAt updatedAt
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
GQL
|
|
59
|
+
SavedPlaylist.from_hash(data[:create_saved_playlist])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @example
|
|
63
|
+
# client.saved_playlists.update("pl_123", name: "Renamed")
|
|
64
|
+
def update(id, name:, description: nil, image: nil, folder_id: nil)
|
|
65
|
+
@http.execute(<<~GQL, { id: id, name: name, description: description, image: image, folder_id: folder_id }.compact)
|
|
66
|
+
mutation UpdateSavedPlaylist(
|
|
67
|
+
$id: String!, $name: String!, $description: String,
|
|
68
|
+
$image: String, $folderId: String
|
|
69
|
+
) {
|
|
70
|
+
updateSavedPlaylist(
|
|
71
|
+
id: $id, name: $name, description: $description,
|
|
72
|
+
image: $image, folderId: $folderId
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
GQL
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def delete(id)
|
|
80
|
+
@http.execute(
|
|
81
|
+
"mutation DeleteSavedPlaylist($id: String!) { deleteSavedPlaylist(id: $id) }",
|
|
82
|
+
{ id: id }
|
|
83
|
+
)
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def add_tracks(playlist_id, track_ids)
|
|
88
|
+
@http.execute(
|
|
89
|
+
"mutation AddTracksToSavedPlaylist($playlistId: String!, $trackIds: [String!]!) { " \
|
|
90
|
+
"addTracksToSavedPlaylist(playlistId: $playlistId, trackIds: $trackIds) }",
|
|
91
|
+
{ playlist_id: playlist_id, track_ids: track_ids }
|
|
92
|
+
)
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def remove_track(playlist_id, track_id)
|
|
97
|
+
@http.execute(
|
|
98
|
+
"mutation RemoveTrackFromSavedPlaylist($playlistId: String!, $trackId: String!) { " \
|
|
99
|
+
"removeTrackFromSavedPlaylist(playlistId: $playlistId, trackId: $trackId) }",
|
|
100
|
+
{ playlist_id: playlist_id, track_id: track_id }
|
|
101
|
+
)
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def play(playlist_id)
|
|
106
|
+
@http.execute(
|
|
107
|
+
"mutation PlaySavedPlaylist($playlistId: String!) { playSavedPlaylist(playlistId: $playlistId) }",
|
|
108
|
+
{ playlist_id: playlist_id }
|
|
109
|
+
)
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------
|
|
114
|
+
# Folders
|
|
115
|
+
# ---------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
def folders
|
|
118
|
+
data = @http.execute("query PlaylistFolders { playlistFolders { id name createdAt updatedAt } }")
|
|
119
|
+
Array(data[:playlist_folders]).map { |f| SavedPlaylistFolder.from_hash(f) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def create_folder(name)
|
|
123
|
+
data = @http.execute(
|
|
124
|
+
"mutation CreatePlaylistFolder($name: String!) { " \
|
|
125
|
+
"createPlaylistFolder(name: $name) { id name createdAt updatedAt } }",
|
|
126
|
+
{ name: name }
|
|
127
|
+
)
|
|
128
|
+
SavedPlaylistFolder.from_hash(data[:create_playlist_folder])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def delete_folder(id)
|
|
132
|
+
@http.execute(
|
|
133
|
+
"mutation DeletePlaylistFolder($id: String!) { deletePlaylistFolder(id: $id) }",
|
|
134
|
+
{ id: id }
|
|
135
|
+
)
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Builder for #create — supports the optional yield-block DSL.
|
|
140
|
+
class CreateBuilder
|
|
141
|
+
attr_accessor :name, :description, :image, :folder_id, :track_ids
|
|
142
|
+
|
|
143
|
+
def initialize(name, description, image, folder_id, track_ids)
|
|
144
|
+
@name = name
|
|
145
|
+
@description = description
|
|
146
|
+
@image = image
|
|
147
|
+
@folder_id = folder_id
|
|
148
|
+
@track_ids = track_ids
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def to_variables
|
|
152
|
+
{
|
|
153
|
+
name: name,
|
|
154
|
+
description: description,
|
|
155
|
+
image: image,
|
|
156
|
+
folder_id: folder_id,
|
|
157
|
+
track_ids: track_ids
|
|
158
|
+
}.compact
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|