rocksky 0.10.0 → 0.11.1
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +26 -1
- data/lib/rocksky/filter.rb +138 -0
- data/lib/rocksky/manifest.json +7 -7
- data/lib/rocksky/playlist.rb +65 -0
- data/lib/rocksky/version.rb +1 -1
- data/lib/rocksky.rb +61 -3
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6285f01e3baa73b2791e6b64a2d4d331aa750abe5f50d1e4cafe6263073bf9ad
|
|
4
|
+
data.tar.gz: 1668269cb59db82bfaf6455e66e493f7dfd9a1c97c5037d9b3eac959a60dd3cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50c4b9e1f82ce55ea8b9370946461dc8fd9bc6c87ce108ac304cf2835a66b0e6474c7cd07cd1c5a77c6794800cc7082a63abb1e45cc8395dfb8b7a07e82d3aed
|
|
7
|
+
data.tar.gz: 57b4fd741109bd4cc218d68520b22fef8f5dc5bfa2f1494ac7439722e892d5e60112eb026bb9cc65f8aaa784a537bbacf9078c939b27b859d646b854b85d1993
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0
|
|
4
|
+
|
|
5
|
+
- Added `Rocksky::Filter`, a fluent RSQL builder (`eq`, `ne`, `gt`, `ge`,
|
|
6
|
+
`lt`, `le`, `is_in`, `is_out`, `is_null`, `is_not_null`, combined with
|
|
7
|
+
`#and` / `#or`). Fields are Symbols (`:artist`, `:"track.artist"`); values
|
|
8
|
+
are quoted/escaped automatically, `*` wildcards stay bare.
|
|
9
|
+
- Added filterable reads: `Rocksky.catalog_songs`, `catalog_artists`,
|
|
10
|
+
`catalog_albums` (`limit:`, `offset:`, `genre:`, `filter:`) and
|
|
11
|
+
`Rocksky.scrobble_feed` (`did:`, `following:`, `limit:`, `offset:`,
|
|
12
|
+
`filter:`). `filter:` takes a `Rocksky::Filter` or a raw RSQL String.
|
|
13
|
+
|
|
3
14
|
## 0.10.0
|
|
4
15
|
|
|
5
16
|
- Remote-player protocol: now-playing records accept optional audio info —
|
data/README.md
CHANGED
|
@@ -14,7 +14,7 @@ gem install rocksky
|
|
|
14
14
|
Or in a Gemfile:
|
|
15
15
|
|
|
16
16
|
```ruby
|
|
17
|
-
gem "rocksky", "~> 0.
|
|
17
|
+
gem "rocksky", "~> 0.11"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
The gem is pure-Ruby; the native library is fetched from the GitHub release on
|
|
@@ -83,6 +83,31 @@ Rocksky.top_artists_interval(limit: 5, interval: :all)
|
|
|
83
83
|
**Match** — `Rocksky.match_song(title, artist, mb_id: nil, isrc: nil)` resolves a
|
|
84
84
|
bare title + artist into full canonical metadata.
|
|
85
85
|
|
|
86
|
+
### Filtering
|
|
87
|
+
|
|
88
|
+
`Rocksky::Filter` builds RSQL expressions for the `filter:` kwarg of
|
|
89
|
+
`catalog_songs`, `catalog_artists`, `catalog_albums` and `scrobble_feed`
|
|
90
|
+
(each also takes `limit:`, `offset:` and — catalogs only — `genre:`). Fields
|
|
91
|
+
are Symbols; dotted selectors on the scrobble feed reach the joined
|
|
92
|
+
track/user/artist (`:"track.artist"`, `:"user.handle"`, …).
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
filter = Rocksky::Filter.eq(:artist, "Daft Punk")
|
|
96
|
+
.and(Rocksky::Filter.gt(:duration, 200_000))
|
|
97
|
+
.or(Rocksky::Filter.is_in(:genre, %w[house electro]))
|
|
98
|
+
filter.to_s # => artist=="Daft Punk";duration=gt=200000,genre=in=(house,electro)
|
|
99
|
+
|
|
100
|
+
Rocksky.catalog_songs(limit: 20, filter: filter)
|
|
101
|
+
Rocksky.scrobble_feed(filter: Rocksky::Filter.eq(:"track.artist", "Daft Punk"))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Constructors: `eq`, `ne`, `gt`, `ge`, `lt`, `le`, `is_in`, `is_out` (aliases
|
|
105
|
+
`in`/`out`), `is_null`, `is_not_null`. Combine with `#and` (`;`) and `#or`
|
|
106
|
+
(`,`); an OR operand inside an AND is parenthesized automatically. String
|
|
107
|
+
values are quoted/escaped when they contain reserved characters, and `*`
|
|
108
|
+
wildcards pass through unquoted (`Filter.eq(:artist, "Daft*")`). A raw RSQL
|
|
109
|
+
String is accepted anywhere a `Filter` is.
|
|
110
|
+
|
|
86
111
|
### Writes — `Rocksky::Agent`
|
|
87
112
|
|
|
88
113
|
`Agent.login(session_path, identifier, password, appview:, dedup_path:)` → an
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Standalone — no native core involved. Loads on its own via
|
|
4
|
+
# `require "rocksky/filter"`.
|
|
5
|
+
module Rocksky
|
|
6
|
+
# Fluent builder for RSQL filter expressions, accepted by the +filter:+
|
|
7
|
+
# parameter of the catalog and scrobble-feed queries
|
|
8
|
+
# (app.rocksky.song.getSongs, app.rocksky.artist.getArtists,
|
|
9
|
+
# app.rocksky.album.getAlbums, app.rocksky.scrobble.getScrobbles).
|
|
10
|
+
#
|
|
11
|
+
# require "rocksky"
|
|
12
|
+
#
|
|
13
|
+
# filter = Rocksky::Filter.eq(:artist, "Daft Punk")
|
|
14
|
+
# .and(Rocksky::Filter.gt(:duration, 200_000))
|
|
15
|
+
# .or(Rocksky::Filter.is_in(:genre, %w[house electro]))
|
|
16
|
+
#
|
|
17
|
+
# Rocksky.catalog_songs(filter: filter)
|
|
18
|
+
# # artist=="Daft Punk";duration=gt=200000,genre=in=(house,electro)
|
|
19
|
+
#
|
|
20
|
+
# Fields are Symbols (Strings work too); dotted selectors on the scrobble
|
|
21
|
+
# feed are written as +:"track.artist"+. String values are quoted and
|
|
22
|
+
# escaped automatically when they contain characters RSQL reserves; +*+
|
|
23
|
+
# wildcards pass through unquoted so <tt>Filter.eq(:artist, "Daft*")</tt>
|
|
24
|
+
# performs a case-insensitive match.
|
|
25
|
+
class Filter
|
|
26
|
+
# Characters that never need quoting in an RSQL value (+*+ kept bare so
|
|
27
|
+
# wildcards work).
|
|
28
|
+
SAFE_VALUE = /\A[A-Za-z0-9_.:@*+-]+\z/
|
|
29
|
+
private_constant :SAFE_VALUE
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# +field==value+ — equals; +*+ in string values is a wildcard.
|
|
33
|
+
def eq(field, value)
|
|
34
|
+
comparison(field, "==", value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# +field!=value+ — not equals.
|
|
38
|
+
def ne(field, value)
|
|
39
|
+
comparison(field, "!=", value)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# +field=gt=value+ — greater than.
|
|
43
|
+
def gt(field, value)
|
|
44
|
+
comparison(field, "=gt=", value)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# +field=ge=value+ — greater than or equal.
|
|
48
|
+
def ge(field, value)
|
|
49
|
+
comparison(field, "=ge=", value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# +field=lt=value+ — less than.
|
|
53
|
+
def lt(field, value)
|
|
54
|
+
comparison(field, "=lt=", value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# +field=le=value+ — less than or equal.
|
|
58
|
+
def le(field, value)
|
|
59
|
+
comparison(field, "=le=", value)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# +field=in=(a,b)+ — matches any of the values.
|
|
63
|
+
def is_in(field, values)
|
|
64
|
+
list(field, "=in=", values)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# +field=out=(a,b)+ — matches none of the values.
|
|
68
|
+
def is_out(field, values)
|
|
69
|
+
list(field, "=out=", values)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias in is_in
|
|
73
|
+
alias out is_out
|
|
74
|
+
|
|
75
|
+
# +field==null+ — the field is NULL.
|
|
76
|
+
def is_null(field)
|
|
77
|
+
new("#{field}==null", :comparison)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# +field!=null+ — the field is not NULL.
|
|
81
|
+
def is_not_null(field)
|
|
82
|
+
new("#{field}!=null", :comparison)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def comparison(field, op, value)
|
|
88
|
+
new("#{field}#{op}#{render_value(value)}", :comparison)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def list(field, op, values)
|
|
92
|
+
name = op == "=in=" ? "is_in" : "is_out"
|
|
93
|
+
raise ArgumentError, "Filter.#{name}(#{field.inspect}, ...) needs at least one value" if values.empty?
|
|
94
|
+
|
|
95
|
+
new("#{field}#{op}(#{values.map { |v| render_value(v) }.join(",")})", :comparison)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def render_value(value)
|
|
99
|
+
return value.to_s if value.is_a?(Integer) || value.is_a?(Float) ||
|
|
100
|
+
value == true || value == false
|
|
101
|
+
|
|
102
|
+
str = value.to_s
|
|
103
|
+
return str if !str.empty? && SAFE_VALUE.match?(str)
|
|
104
|
+
|
|
105
|
+
%("#{str.gsub(/(["\\])/) { "\\#{Regexp.last_match(1)}" }}")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def initialize(expr, kind)
|
|
110
|
+
@expr = expr
|
|
111
|
+
@kind = kind
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Both sides must match (+;+). An +or+ operand is parenthesized to keep
|
|
115
|
+
# RSQL precedence.
|
|
116
|
+
def and(other)
|
|
117
|
+
Filter.new("#{operand_in_and};#{other.operand_in_and}", :and)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Either side may match (+,+). Never parenthesizes.
|
|
121
|
+
def or(other)
|
|
122
|
+
Filter.new("#{@expr},#{other.build}", :or)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The RSQL expression string to send as the +filter+ query param.
|
|
126
|
+
def build
|
|
127
|
+
@expr
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
alias to_s build
|
|
131
|
+
|
|
132
|
+
protected
|
|
133
|
+
|
|
134
|
+
def operand_in_and
|
|
135
|
+
@kind == :or ? "(#{@expr})" : @expr
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/lib/rocksky/manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "tsirysndr/rocksky",
|
|
3
|
-
"tag": "bindings-v0.
|
|
3
|
+
"tag": "bindings-v0.10.0",
|
|
4
4
|
"checksums": {
|
|
5
|
-
"aarch64-apple-darwin": "
|
|
6
|
-
"aarch64-linux-gnu": "
|
|
7
|
-
"x86_64-apple-darwin": "
|
|
8
|
-
"x86_64-linux-gnu": "
|
|
9
|
-
"x86_64-unknown-freebsd": "
|
|
10
|
-
"x86_64-unknown-netbsd": "
|
|
5
|
+
"aarch64-apple-darwin": "36607cf75cc05b79f93570efae0047dfe58f4beff38033f59e9c8c9e03c0e62e",
|
|
6
|
+
"aarch64-linux-gnu": "d5fcf8a3ca7acba5eaf45db0478360ade6ebfcea7f1dab582ff10696ec77c24e",
|
|
7
|
+
"x86_64-apple-darwin": "a19daa282e01fa68cb279da28bba52198c928c7771770d4e0a5e10e922a51aa9",
|
|
8
|
+
"x86_64-linux-gnu": "a6751be73f81d32639b85f3a1f2c6420b7a2346acbc59a5fd534156915d2f918",
|
|
9
|
+
"x86_64-unknown-freebsd": "e811d322e14ce51498928616c02f0c6228ea765a7f67efef00963a1bbe0c172e",
|
|
10
|
+
"x86_64-unknown-netbsd": "ce8e555d3ef6caee7dd7c7302feb14b73268ae46dcc9c517d8370c57c93624c6"
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rocksky
|
|
4
|
+
# app.rocksky.playlist.* — the global, AT-Proto-backed playlists.
|
|
5
|
+
#
|
|
6
|
+
# Distinct from Rocksky::Library's playlist methods, which drive the
|
|
7
|
+
# Subsonic/Navidrome library (app.rocksky.library.*).
|
|
8
|
+
#
|
|
9
|
+
# Writes publish records to the caller's repo, so they need a +token+ and only
|
|
10
|
+
# show up in reads once the AppView has ingested the commit.
|
|
11
|
+
module Playlist
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# The playlist catalog.
|
|
15
|
+
def list(limit: 50, offset: 0, filter: nil, base: nil)
|
|
16
|
+
params = { limit: limit, offset: offset }
|
|
17
|
+
params[:filter] = filter if filter
|
|
18
|
+
Rocksky.get("app.rocksky.playlist.getPlaylists", params, base: base)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# A single playlist with its tracks. +filter+ is an RSQL expression over the
|
|
22
|
+
# tracks (e.g. 'artist=="Daft Punk"').
|
|
23
|
+
def get(uri, filter: nil, base: nil)
|
|
24
|
+
params = { uri: uri }
|
|
25
|
+
params[:filter] = filter if filter
|
|
26
|
+
Rocksky.get("app.rocksky.playlist.getPlaylist", params, base: base)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Create a playlist. Returns { "uri" => ..., "cid" => ... }.
|
|
30
|
+
def create(name, token:, description: nil, picture_url: nil, base: nil)
|
|
31
|
+
params = { name: name }
|
|
32
|
+
params[:description] = description if description
|
|
33
|
+
params[:pictureUrl] = picture_url if picture_url
|
|
34
|
+
Rocksky.post("app.rocksky.playlist.createPlaylist", params, base: base, token: token)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Rename or re-describe a playlist. Owner only.
|
|
38
|
+
def update(uri, token:, name: nil, description: nil, picture_url: nil, base: nil)
|
|
39
|
+
params = { uri: uri }
|
|
40
|
+
params[:name] = name if name
|
|
41
|
+
params[:description] = description if description
|
|
42
|
+
params[:pictureUrl] = picture_url if picture_url
|
|
43
|
+
Rocksky.post("app.rocksky.playlist.updatePlaylist", params, base: base, token: token)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Add songs by their app.rocksky.song AT-URIs. Owner only. Returns
|
|
47
|
+
# { "uris" => [...] } — the created entry records.
|
|
48
|
+
def add_songs(uri, songs, token:, base: nil)
|
|
49
|
+
Rocksky.post("app.rocksky.playlist.addSongs", { uri: uri, songs: songs },
|
|
50
|
+
base: base, token: token)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Remove a song. An entry lives in the repo that added it, so only that repo
|
|
54
|
+
# can retract it.
|
|
55
|
+
def remove_track(uri, song_uri, token:, base: nil)
|
|
56
|
+
Rocksky.post("app.rocksky.playlist.removeTrack", { uri: uri, songUri: song_uri },
|
|
57
|
+
base: base, token: token)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Delete a playlist and the caller's own entries. Owner only.
|
|
61
|
+
def remove(uri, token:, base: nil)
|
|
62
|
+
Rocksky.post("app.rocksky.playlist.removePlaylist", { uri: uri }, base: base, token: token)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/rocksky/version.rb
CHANGED
data/lib/rocksky.rb
CHANGED
|
@@ -27,10 +27,11 @@ module Rocksky
|
|
|
27
27
|
"char* rocksky_top_tracks(const char*, unsigned int, unsigned int)",
|
|
28
28
|
"char* rocksky_global_stats(const char*)",
|
|
29
29
|
"char* rocksky_get(const char*, const char*, const char*, const char*)",
|
|
30
|
+
"char* rocksky_post(const char*, const char*, const char*, const char*)",
|
|
30
31
|
"char* rocksky_update_seen(const char*, const char*, const char*)",
|
|
31
32
|
"char* rocksky_library_get(const char*, const char*, const char*, const char*)",
|
|
32
33
|
"char* rocksky_library_post(const char*, const char*, const char*, const char*)",
|
|
33
|
-
"char* rocksky_match_song(const char*, const char*, const char*, const char*, const char*)",
|
|
34
|
+
"char* rocksky_match_song(const char*, const char*, const char*, const char*, const char*, const char*)",
|
|
34
35
|
"char* rocksky_top_tracks_interval(const char*, unsigned int, unsigned int, const char*, unsigned int, const char*, const char*)",
|
|
35
36
|
"char* rocksky_top_artists_interval(const char*, unsigned int, unsigned int, const char*, unsigned int, const char*, const char*)",
|
|
36
37
|
"char* rocksky_song_hash(const char*, const char*, const char*)",
|
|
@@ -127,6 +128,51 @@ module Rocksky
|
|
|
127
128
|
unwrap(C.rocksky_global_stats(base.to_s))
|
|
128
129
|
end
|
|
129
130
|
|
|
131
|
+
# ---- catalog + feed reads with RSQL filtering ----
|
|
132
|
+
#
|
|
133
|
+
# +filter:+ accepts a Rocksky::Filter (see rocksky/filter.rb) or a raw RSQL
|
|
134
|
+
# String. Fields are Symbols, e.g. Rocksky::Filter.eq(:artist, "Daft Punk").
|
|
135
|
+
|
|
136
|
+
# The song catalog (app.rocksky.song.getSongs), optionally narrowed by
|
|
137
|
+
# +genre:+ and/or an RSQL +filter:+. Returns { "tracks" => [...] }.
|
|
138
|
+
def self.catalog_songs(limit: 50, offset: 0, genre: nil, filter: nil, base: nil)
|
|
139
|
+
get("app.rocksky.song.getSongs", catalog_params(limit, offset, genre, filter), base: base)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# The artist catalog (app.rocksky.artist.getArtists). Returns
|
|
143
|
+
# { "artists" => [...] }.
|
|
144
|
+
def self.catalog_artists(limit: 50, offset: 0, genre: nil, filter: nil, base: nil)
|
|
145
|
+
get("app.rocksky.artist.getArtists", catalog_params(limit, offset, genre, filter), base: base)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# The album catalog (app.rocksky.album.getAlbums). Returns
|
|
149
|
+
# { "albums" => [...] }.
|
|
150
|
+
def self.catalog_albums(limit: 50, offset: 0, genre: nil, filter: nil, base: nil)
|
|
151
|
+
get("app.rocksky.album.getAlbums", catalog_params(limit, offset, genre, filter), base: base)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# A social/global scrobbles feed (app.rocksky.scrobble.getScrobbles). Pass
|
|
155
|
+
# +did:+ to scope to an actor and +following: true+ for their follow graph.
|
|
156
|
+
# +filter:+ reaches the joined track/user/artist via dotted fields, e.g.
|
|
157
|
+
# Rocksky::Filter.eq(:"track.artist", "Daft Punk"). Returns
|
|
158
|
+
# { "scrobbles" => [...] }.
|
|
159
|
+
def self.scrobble_feed(did: nil, following: false, limit: 50, offset: 0, filter: nil, base: nil)
|
|
160
|
+
params = { limit: limit, offset: offset }
|
|
161
|
+
params[:did] = did unless did.nil?
|
|
162
|
+
params[:following] = following if following
|
|
163
|
+
params[:filter] = filter.to_s unless filter.nil?
|
|
164
|
+
get("app.rocksky.scrobble.getScrobbles", params, base: base)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Shared param assembly for the three catalog reads (omit-nil idiom).
|
|
168
|
+
def self.catalog_params(limit, offset, genre, filter)
|
|
169
|
+
params = { limit: limit, offset: offset }
|
|
170
|
+
params[:genre] = genre unless genre.nil?
|
|
171
|
+
params[:filter] = filter.to_s unless filter.nil?
|
|
172
|
+
params
|
|
173
|
+
end
|
|
174
|
+
private_class_method :catalog_params
|
|
175
|
+
|
|
130
176
|
# Universal read escape hatch — call any app.rocksky.* query by nsid. +params+
|
|
131
177
|
# is a hash of string params; the whole read-query catalog is reachable here.
|
|
132
178
|
#
|
|
@@ -138,6 +184,14 @@ module Rocksky
|
|
|
138
184
|
unwrap(C.rocksky_get(base.to_s, nsid, JSON.generate(params), token.to_s))
|
|
139
185
|
end
|
|
140
186
|
|
|
187
|
+
# Universal write escape hatch — call any app.rocksky.* procedure whose
|
|
188
|
+
# arguments ride the query string. Most are auth-gated, so pass +token+.
|
|
189
|
+
#
|
|
190
|
+
# Rocksky.post("app.rocksky.playlist.removePlaylist", { uri: uri }, token: tok)
|
|
191
|
+
def self.post(nsid, params = {}, base: nil, token: nil)
|
|
192
|
+
unwrap(C.rocksky_post(base.to_s, nsid, JSON.generate(params), token.to_s))
|
|
193
|
+
end
|
|
194
|
+
|
|
141
195
|
# ---- notifications (auth-gated; +token+ required) ----
|
|
142
196
|
|
|
143
197
|
# The authenticated viewer's unread-notification count. Returns
|
|
@@ -163,8 +217,10 @@ module Rocksky
|
|
|
163
217
|
end
|
|
164
218
|
|
|
165
219
|
# Resolve full canonical metadata for a bare title + artist (matchSong).
|
|
166
|
-
|
|
167
|
-
|
|
220
|
+
# +album+ steers the match toward that release (case-insensitive) so a
|
|
221
|
+
# remaster/live/single edition doesn't shadow the intended album.
|
|
222
|
+
def self.match_song(title, artist, album: nil, mb_id: nil, isrc: nil, base: nil)
|
|
223
|
+
unwrap(C.rocksky_match_song(base.to_s, title, artist, album.to_s, mb_id.to_s, isrc.to_s))
|
|
168
224
|
end
|
|
169
225
|
|
|
170
226
|
# Top tracks chart over a typed date window. +interval+ is +:all+, or a pair
|
|
@@ -301,5 +357,7 @@ module Rocksky
|
|
|
301
357
|
end
|
|
302
358
|
end
|
|
303
359
|
|
|
360
|
+
require_relative "rocksky/filter"
|
|
304
361
|
require_relative "rocksky/library"
|
|
362
|
+
require_relative "rocksky/playlist"
|
|
305
363
|
require_relative "rocksky/remote"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rocksky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rocksky
|
|
@@ -80,9 +80,11 @@ files:
|
|
|
80
80
|
- README.md
|
|
81
81
|
- exe/rocksky-console
|
|
82
82
|
- lib/rocksky.rb
|
|
83
|
+
- lib/rocksky/filter.rb
|
|
83
84
|
- lib/rocksky/library.rb
|
|
84
85
|
- lib/rocksky/manifest.json
|
|
85
86
|
- lib/rocksky/native.rb
|
|
87
|
+
- lib/rocksky/playlist.rb
|
|
86
88
|
- lib/rocksky/remote.rb
|
|
87
89
|
- lib/rocksky/version.rb
|
|
88
90
|
- rocksky.gemspec
|