rocksky 0.3.0 → 0.4.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.
@@ -1,84 +0,0 @@
1
- module Rocksky
2
- # Top-level client for the Rocksky XRPC API.
3
- #
4
- # client = Rocksky.new(token: ENV["ROCKSKY_TOKEN"])
5
- # client.actor.get_profile(did: "alice.bsky.social")
6
- #
7
- # Resources are lazily instantiated and memoised: `client.actor`, `client.album`,
8
- # `client.artist`, `client.scrobble`, etc.
9
- class Client
10
- DEFAULT_BASE_URL = "https://api.rocksky.app".freeze
11
-
12
- attr_reader :base_url, :token, :headers, :user_agent,
13
- :open_timeout, :read_timeout
14
-
15
- def initialize(
16
- base_url: nil,
17
- token: nil,
18
- headers: {},
19
- user_agent: "rocksky-ruby/#{Rocksky::VERSION}",
20
- open_timeout: HTTP::DEFAULT_OPEN_TIMEOUT,
21
- read_timeout: HTTP::DEFAULT_READ_TIMEOUT
22
- )
23
- @base_url = normalize_base_url(base_url || ENV["ROCKSKY_BASE_URL"] || DEFAULT_BASE_URL)
24
- @token = token || ENV["ROCKSKY_TOKEN"]
25
- @headers = headers.dup
26
- @user_agent = user_agent
27
- @open_timeout = open_timeout
28
- @read_timeout = read_timeout
29
- @http = HTTP.new(self)
30
- end
31
-
32
- # Return a derived client that uses the given token (everything else copied).
33
- # Handy for sharing one client across users in a request-scoped server.
34
- def with_token(new_token)
35
- self.class.new(
36
- base_url: base_url,
37
- token: new_token,
38
- headers: headers,
39
- user_agent: user_agent,
40
- open_timeout: open_timeout,
41
- read_timeout: read_timeout
42
- )
43
- end
44
-
45
- # ---- Raw XRPC access ---------------------------------------------------
46
-
47
- def query(nsid, **params)
48
- @http.query(nsid, params)
49
- end
50
-
51
- def procedure(nsid, params: {}, body: nil)
52
- @http.procedure(nsid, params, body)
53
- end
54
-
55
- # ---- Resource accessors ------------------------------------------------
56
-
57
- def actor; @actor ||= Resources::Actor.new(@http); end
58
- def album; @album ||= Resources::Album.new(@http); end
59
- def apikey; @apikey ||= Resources::Apikey.new(@http); end
60
- def artist; @artist ||= Resources::Artist.new(@http); end
61
- def charts; @charts ||= Resources::Charts.new(@http); end
62
- def feed; @feed ||= Resources::Feed.new(@http); end
63
- def graph; @graph ||= Resources::Graph.new(@http); end
64
- def like; @like ||= Resources::Like.new(@http); end
65
- def mirror; @mirror ||= Resources::Mirror.new(@http); end
66
- def player; @player ||= Resources::Player.new(@http); end
67
- def playlist; @playlist ||= Resources::Playlist.new(@http); end
68
- def scrobble; @scrobble ||= Resources::Scrobble.new(@http); end
69
- def shout; @shout ||= Resources::Shout.new(@http); end
70
- def song; @song ||= Resources::Song.new(@http); end
71
- def spotify; @spotify ||= Resources::Spotify.new(@http); end
72
- def stats; @stats ||= Resources::Stats.new(@http); end
73
-
74
- def inspect
75
- "#<Rocksky::Client base_url=#{base_url.inspect} token=#{token ? "[FILTERED]" : nil.inspect}>"
76
- end
77
-
78
- private
79
-
80
- def normalize_base_url(url)
81
- url.to_s.sub(%r{/+\z}, "")
82
- end
83
- end
84
- end
data/lib/rocksky/error.rb DELETED
@@ -1,58 +0,0 @@
1
- module Rocksky
2
- class Error < StandardError
3
- attr_reader :status, :body, :nsid
4
-
5
- def initialize(message, status: nil, body: nil, nsid: nil)
6
- super(message)
7
- @status = status
8
- @body = body
9
- @nsid = nsid
10
- end
11
-
12
- def self.from_response(status, body, nsid: nil)
13
- message = extract_message(body) || "request failed"
14
- klass = klass_for(status)
15
- klass.new(message, status: status, body: body, nsid: nsid)
16
- end
17
-
18
- def self.from_transport(cause, nsid: nil)
19
- TransportError.new(
20
- "transport error: #{cause.class}: #{cause.message}",
21
- body: nil,
22
- nsid: nsid
23
- )
24
- end
25
-
26
- def self.klass_for(status)
27
- case status
28
- when 400 then BadRequest
29
- when 401 then Unauthorized
30
- when 403 then Forbidden
31
- when 404 then NotFound
32
- when 429 then RateLimited
33
- when 500..599 then ServerError
34
- else HTTPError
35
- end
36
- end
37
- private_class_method :klass_for
38
-
39
- def self.extract_message(body)
40
- case body
41
- when Hash
42
- body["message"] || body["error"] || body[:message] || body[:error]
43
- when String
44
- body unless body.empty?
45
- end
46
- end
47
- private_class_method :extract_message
48
- end
49
-
50
- class HTTPError < Error; end
51
- class BadRequest < HTTPError; end
52
- class Unauthorized < HTTPError; end
53
- class Forbidden < HTTPError; end
54
- class NotFound < HTTPError; end
55
- class RateLimited < HTTPError; end
56
- class ServerError < HTTPError; end
57
- class TransportError < Error; end
58
- end
@@ -1,219 +0,0 @@
1
- # frozen_string_literal: true
2
- # AUTO-GENERATED FILE -- DO NOT EDIT.
3
- # Source: apps/api/lexicons/**/*.json
4
- # Regenerate via: bun run lexgen:types
5
-
6
- module Rocksky
7
- module Generated
8
- BlobRef = Struct.new(:type, :ref, :mimeType, :size, keyword_init: true)
9
-
10
- ActorArtistViewBasic = Struct.new(:id, :name, :picture, :uri, :user1Rank, :user2Rank, :weight, keyword_init: true)
11
- ActorCompatibilityViewBasic = Struct.new(:compatibilityLevel, :compatibilityPercentage, :sharedArtists, :topSharedArtistNames, :topSharedDetailedArtists, :user1ArtistCount, :user2ArtistCount, keyword_init: true)
12
- ActorNeighbourViewBasic = Struct.new(:userId, :did, :handle, :displayName, :avatar, :sharedArtistsCount, :similarityScore, :topSharedArtistNames, :topSharedArtistsDetails, keyword_init: true)
13
- ActorProfileViewBasic = Struct.new(:id, :did, :handle, :displayName, :avatar, :createdAt, :updatedAt, keyword_init: true)
14
- ActorProfileViewDetailed = Struct.new(:id, :did, :handle, :displayName, :avatar, :createdAt, :updatedAt, keyword_init: true)
15
- ActorTrackView = Struct.new(:name, :artist, :album, :albumCoverUrl, :durationMs, :source, :recordingMbId, keyword_init: true)
16
- AddDirectoryToQueueParams = Struct.new(:playerId, :directory, :position, :shuffle, keyword_init: true)
17
- AddItemsToQueueParams = Struct.new(:playerId, :items, :position, :shuffle, keyword_init: true)
18
- AlbumRecord = Struct.new(:title, :artist, :duration, :releaseDate, :year, :genre, :albumArt, :albumArtUrl, :tags, :youtubeLink, :spotifyLink, :tidalLink, :appleMusicLink, :createdAt, keyword_init: true)
19
- AlbumViewBasic = Struct.new(:id, :uri, :title, :artist, :artistUri, :year, :albumArt, :releaseDate, :sha256, :playCount, :uniqueListeners, keyword_init: true)
20
- AlbumViewDetailed = Struct.new(:id, :uri, :title, :artist, :artistUri, :year, :albumArt, :releaseDate, :sha256, :playCount, :uniqueListeners, :tags, :tracks, keyword_init: true)
21
- ApiKeyView = Struct.new(:id, :name, :description, :createdAt, keyword_init: true)
22
- ArtistListenerViewBasic = Struct.new(:id, :did, :handle, :displayName, :avatar, :mostListenedSong, :totalPlays, :rank, keyword_init: true)
23
- ArtistMbid = Struct.new(:mbid, :name, keyword_init: true)
24
- ArtistRecentListenerView = Struct.new(:id, :did, :handle, :displayName, :avatar, :timestamp, :scrobbleUri, keyword_init: true)
25
- ArtistRecord = Struct.new(:name, :bio, :picture, :pictureUrl, :tags, :born, :died, :bornIn, :createdAt, keyword_init: true)
26
- ArtistSongViewBasic = Struct.new(:uri, :title, :playCount, keyword_init: true)
27
- ArtistViewBasic = Struct.new(:id, :uri, :name, :picture, :sha256, :playCount, :uniqueListeners, :tags, keyword_init: true)
28
- ArtistViewDetailed = Struct.new(:id, :uri, :name, :picture, :sha256, :playCount, :uniqueListeners, :tags, keyword_init: true)
29
- ChartsScrobbleViewBasic = Struct.new(:date, :count, keyword_init: true)
30
- ChartsView = Struct.new(:scrobbles, keyword_init: true)
31
- CreateApikeyInput = Struct.new(:name, :description, keyword_init: true)
32
- CreatePlaylistParams = Struct.new(:name, :description, keyword_init: true)
33
- CreateScrobbleInput = Struct.new(:title, :artist, :album, :duration, :mbId, :isrc, :albumArt, :trackNumber, :releaseDate, :year, :discNumber, :lyrics, :composer, :copyrightMessage, :label, :artistPicture, :spotifyLink, :lastfmLink, :tidalLink, :appleMusicLink, :youtubeLink, :deezerLink, :timestamp, keyword_init: true)
34
- CreateShoutInput = Struct.new(:message, keyword_init: true)
35
- CreateSongInput = Struct.new(:title, :artist, :albumArtist, :album, :duration, :mbId, :isrc, :albumArt, :trackNumber, :releaseDate, :year, :discNumber, :lyrics, keyword_init: true)
36
- DescribeFeedGeneratorOutput = Struct.new(:did, :feeds, keyword_init: true)
37
- DescribeFeedGeneratorParams = Struct.new(keyword_init: true)
38
- DislikeShoutInput = Struct.new(:uri, keyword_init: true)
39
- DislikeSongInput = Struct.new(:uri, keyword_init: true)
40
- DownloadFileParams = Struct.new(:fileId, keyword_init: true)
41
- DropboxFileListView = Struct.new(:files, keyword_init: true)
42
- DropboxFileView = Struct.new(:id, :name, :pathLower, :pathDisplay, :clientModified, :serverModified, keyword_init: true)
43
- DropboxTemporaryLinkView = Struct.new(:link, keyword_init: true)
44
- FeedGeneratorsView = Struct.new(:feeds, keyword_init: true)
45
- FeedGeneratorView = Struct.new(:id, :name, :description, :uri, :avatar, :creator, keyword_init: true)
46
- FeedItemView = Struct.new(:scrobble, keyword_init: true)
47
- FeedRecommendationsView = Struct.new(:recommendations, :cursor, keyword_init: true)
48
- FeedRecommendationView = Struct.new(:title, :artist, :album, :albumArt, :trackUri, :artistUri, :albumUri, :genres, :recommendationScore, :source, :likesCount, keyword_init: true)
49
- FeedRecommendedAlbumsView = Struct.new(:albums, :cursor, keyword_init: true)
50
- FeedRecommendedAlbumView = Struct.new(:id, :uri, :title, :artist, :artistUri, :year, :albumArt, :recommendationScore, :source, keyword_init: true)
51
- FeedRecommendedArtistsView = Struct.new(:artists, :cursor, keyword_init: true)
52
- FeedRecommendedArtistView = Struct.new(:id, :uri, :name, :picture, :genres, :recommendationScore, :source, keyword_init: true)
53
- FeedSearchResultsView = Struct.new(:hits, :processingTimeMs, :limit, :offset, :estimatedTotalHits, keyword_init: true)
54
- FeedStoriesView = Struct.new(:stories, keyword_init: true)
55
- FeedStoryView = Struct.new(:album, :albumArt, :albumArtist, :albumUri, :artist, :artistUri, :avatar, :createdAt, :did, :handle, :id, :title, :trackId, :trackUri, :uri, keyword_init: true)
56
- FeedUriView = Struct.new(:uri, keyword_init: true)
57
- FeedView = Struct.new(:feed, :cursor, keyword_init: true)
58
- FollowAccountOutput = Struct.new(:subject, :followers, :cursor, keyword_init: true)
59
- FollowAccountParams = Struct.new(:account, keyword_init: true)
60
- FollowRecord = Struct.new(:createdAt, :subject, :via, keyword_init: true)
61
- GeneratorRecord = Struct.new(:did, :avatar, :displayName, :description, :createdAt, keyword_init: true)
62
- GetActorAlbumsOutput = Struct.new(:albums, keyword_init: true)
63
- GetActorAlbumsParams = Struct.new(:did, :limit, :offset, :startDate, :endDate, keyword_init: true)
64
- GetActorArtistsOutput = Struct.new(:artists, keyword_init: true)
65
- GetActorArtistsParams = Struct.new(:did, :limit, :offset, :startDate, :endDate, keyword_init: true)
66
- GetActorCompatibilityOutput = Struct.new(:compatibility, keyword_init: true)
67
- GetActorCompatibilityParams = Struct.new(:did, keyword_init: true)
68
- GetActorLovedSongsOutput = Struct.new(:tracks, keyword_init: true)
69
- GetActorLovedSongsParams = Struct.new(:did, :limit, :offset, keyword_init: true)
70
- GetActorNeighboursOutput = Struct.new(:neighbours, keyword_init: true)
71
- GetActorNeighboursParams = Struct.new(:did, keyword_init: true)
72
- GetActorPlaylistsOutput = Struct.new(:playlists, keyword_init: true)
73
- GetActorPlaylistsParams = Struct.new(:did, :limit, :offset, keyword_init: true)
74
- GetActorScrobblesOutput = Struct.new(:scrobbles, keyword_init: true)
75
- GetActorScrobblesParams = Struct.new(:did, :limit, :offset, keyword_init: true)
76
- GetActorSongsOutput = Struct.new(:songs, keyword_init: true)
77
- GetActorSongsParams = Struct.new(:did, :limit, :offset, :startDate, :endDate, keyword_init: true)
78
- GetAlbumParams = Struct.new(:uri, keyword_init: true)
79
- GetAlbumRecommendationsParams = Struct.new(:did, :limit, keyword_init: true)
80
- GetAlbumShoutsOutput = Struct.new(:shouts, keyword_init: true)
81
- GetAlbumShoutsParams = Struct.new(:uri, :limit, :offset, keyword_init: true)
82
- GetAlbumsOutput = Struct.new(:albums, keyword_init: true)
83
- GetAlbumsParams = Struct.new(:limit, :offset, :genre, keyword_init: true)
84
- GetAlbumTracksOutput = Struct.new(:tracks, keyword_init: true)
85
- GetAlbumTracksParams = Struct.new(:uri, keyword_init: true)
86
- GetApikeysOutput = Struct.new(:apiKeys, keyword_init: true)
87
- GetApikeysParams = Struct.new(:offset, :limit, keyword_init: true)
88
- GetArtistAlbumsOutput = Struct.new(:albums, keyword_init: true)
89
- GetArtistAlbumsParams = Struct.new(:uri, keyword_init: true)
90
- GetArtistListenersOutput = Struct.new(:listeners, keyword_init: true)
91
- GetArtistListenersParams = Struct.new(:uri, :offset, :limit, keyword_init: true)
92
- GetArtistParams = Struct.new(:uri, keyword_init: true)
93
- GetArtistRecentListenersOutput = Struct.new(:listeners, keyword_init: true)
94
- GetArtistRecentListenersParams = Struct.new(:uri, :offset, :limit, keyword_init: true)
95
- GetArtistRecommendationsParams = Struct.new(:did, :limit, keyword_init: true)
96
- GetArtistShoutsOutput = Struct.new(:shouts, keyword_init: true)
97
- GetArtistShoutsParams = Struct.new(:uri, :limit, :offset, keyword_init: true)
98
- GetArtistsOutput = Struct.new(:artists, keyword_init: true)
99
- GetArtistsParams = Struct.new(:limit, :offset, :names, :genre, keyword_init: true)
100
- GetArtistTracksOutput = Struct.new(:tracks, keyword_init: true)
101
- GetArtistTracksParams = Struct.new(:uri, :limit, :offset, keyword_init: true)
102
- GetCurrentlyPlayingParams = Struct.new(:playerId, :actor, keyword_init: true)
103
- GetFeedGeneratorOutput = Struct.new(:view, keyword_init: true)
104
- GetFeedGeneratorParams = Struct.new(:feed, keyword_init: true)
105
- GetFeedGeneratorsParams = Struct.new(:size, keyword_init: true)
106
- GetFeedParams = Struct.new(:feed, :limit, :cursor, keyword_init: true)
107
- GetFeedSkeletonOutput = Struct.new(:scrobbles, :cursor, keyword_init: true)
108
- GetFeedSkeletonParams = Struct.new(:feed, :limit, :offset, :cursor, keyword_init: true)
109
- GetFileParams = Struct.new(:fileId, keyword_init: true)
110
- GetFilesParams = Struct.new(:at, keyword_init: true)
111
- GetFollowersOutput = Struct.new(:subject, :followers, :cursor, :count, keyword_init: true)
112
- GetFollowersParams = Struct.new(:actor, :limit, :dids, :cursor, keyword_init: true)
113
- GetFollowsOutput = Struct.new(:subject, :follows, :cursor, :count, keyword_init: true)
114
- GetFollowsParams = Struct.new(:actor, :limit, :dids, :cursor, keyword_init: true)
115
- GetGlobalStatsParams = Struct.new(keyword_init: true)
116
- GetKnownFollowersOutput = Struct.new(:subject, :followers, :cursor, keyword_init: true)
117
- GetKnownFollowersParams = Struct.new(:actor, :limit, :cursor, keyword_init: true)
118
- GetMetadataParams = Struct.new(:path, keyword_init: true)
119
- GetMirrorSourcesOutput = Struct.new(:sources, keyword_init: true)
120
- GetMirrorSourcesParams = Struct.new(keyword_init: true)
121
- GetPlaybackQueueParams = Struct.new(:playerId, keyword_init: true)
122
- GetPlaylistParams = Struct.new(:uri, keyword_init: true)
123
- GetPlaylistsOutput = Struct.new(:playlists, keyword_init: true)
124
- GetPlaylistsParams = Struct.new(:limit, :offset, keyword_init: true)
125
- GetProfileParams = Struct.new(:did, keyword_init: true)
126
- GetProfileShoutsOutput = Struct.new(:shouts, keyword_init: true)
127
- GetProfileShoutsParams = Struct.new(:did, :offset, :limit, keyword_init: true)
128
- GetRecommendationsParams = Struct.new(:did, :limit, keyword_init: true)
129
- GetScrobbleParams = Struct.new(:uri, keyword_init: true)
130
- GetScrobblesChartParams = Struct.new(:did, :artisturi, :albumuri, :songuri, :genre, :from, :to, keyword_init: true)
131
- GetScrobblesOutput = Struct.new(:scrobbles, keyword_init: true)
132
- GetScrobblesParams = Struct.new(:did, :following, :limit, :offset, keyword_init: true)
133
- GetShoutRepliesOutput = Struct.new(:shouts, keyword_init: true)
134
- GetShoutRepliesParams = Struct.new(:uri, :limit, :offset, keyword_init: true)
135
- GetSongParams = Struct.new(:uri, :mbid, :isrc, :spotifyId, keyword_init: true)
136
- GetSongRecentListenersOutput = Struct.new(:listeners, keyword_init: true)
137
- GetSongRecentListenersParams = Struct.new(:uri, :offset, :limit, keyword_init: true)
138
- GetSongsOutput = Struct.new(:songs, keyword_init: true)
139
- GetSongsParams = Struct.new(:limit, :offset, :genre, :mbid, :isrc, :spotifyId, keyword_init: true)
140
- GetStatsParams = Struct.new(:did, keyword_init: true)
141
- GetStoriesParams = Struct.new(:size, :feed, :following, keyword_init: true)
142
- GetTemporaryLinkParams = Struct.new(:path, keyword_init: true)
143
- GetTopArtistsOutput = Struct.new(:artists, keyword_init: true)
144
- GetTopArtistsParams = Struct.new(:limit, :offset, :startDate, :endDate, keyword_init: true)
145
- GetTopTracksOutput = Struct.new(:tracks, keyword_init: true)
146
- GetTopTracksParams = Struct.new(:limit, :offset, :startDate, :endDate, keyword_init: true)
147
- GetTrackShoutsOutput = Struct.new(:shouts, keyword_init: true)
148
- GetTrackShoutsParams = Struct.new(:uri, keyword_init: true)
149
- GetWrappedParams = Struct.new(:did, :year, keyword_init: true)
150
- GoogledriveFileListView = Struct.new(:files, keyword_init: true)
151
- GoogledriveFileView = Struct.new(:id, keyword_init: true)
152
- # indicates that a handle or DID could not be resolved
153
- GraphNotFoundActor = Struct.new(:actor, :notFound, keyword_init: true)
154
- GraphRelationship = Struct.new(:did, :following, :followedBy, keyword_init: true)
155
- InsertDirectoryParams = Struct.new(:uri, :directory, :position, keyword_init: true)
156
- InsertFilesParams = Struct.new(:uri, :files, :position, keyword_init: true)
157
- LikeRecord = Struct.new(:createdAt, :subject, keyword_init: true)
158
- LikeShoutInput = Struct.new(:uri, keyword_init: true)
159
- LikeSongInput = Struct.new(:uri, keyword_init: true)
160
- MatchSongParams = Struct.new(:title, :artist, :mbId, :isrc, keyword_init: true)
161
- MirrorSourceView = Struct.new(:provider, :enabled, :externalUsername, :hasCredentials, :lastPolledAt, :lastScrobbleSeenAt, keyword_init: true)
162
- NextParams = Struct.new(:playerId, keyword_init: true)
163
- PauseParams = Struct.new(:playerId, keyword_init: true)
164
- PlayDirectoryParams = Struct.new(:playerId, :directoryId, :shuffle, :recurse, :position, keyword_init: true)
165
- PlayerCurrentlyPlayingViewDetailed = Struct.new(:title, keyword_init: true)
166
- PlayerPlaybackQueueViewDetailed = Struct.new(:tracks, keyword_init: true)
167
- PlayFileParams = Struct.new(:playerId, :fileId, keyword_init: true)
168
- PlaylistItemRecord = Struct.new(:subject, :createdAt, :track, :order, keyword_init: true)
169
- PlaylistRecord = Struct.new(:name, :description, :picture, :pictureUrl, :createdAt, :spotifyLink, :tidalLink, :youtubeLink, :appleMusicLink, keyword_init: true)
170
- # Basic view of a playlist, including its metadata
171
- PlaylistViewBasic = Struct.new(:id, :title, :uri, :curatorDid, :curatorHandle, :curatorName, :curatorAvatarUrl, :description, :coverImageUrl, :createdAt, :trackCount, keyword_init: true)
172
- # Detailed view of a playlist, including its tracks and metadata
173
- PlaylistViewDetailed = Struct.new(:id, :title, :uri, :curatorDid, :curatorHandle, :curatorName, :curatorAvatarUrl, :description, :coverImageUrl, :createdAt, :tracks, keyword_init: true)
174
- PlayParams = Struct.new(:playerId, keyword_init: true)
175
- PreviousParams = Struct.new(:playerId, keyword_init: true)
176
- ProfileRecord = Struct.new(:displayName, :description, :avatar, :banner, :labels, :joinedViaStarterPack, :createdAt, keyword_init: true)
177
- PutMirrorSourceInput = Struct.new(:provider, :enabled, :externalUsername, :apiKey, keyword_init: true)
178
- RadioRecord = Struct.new(:name, :url, :description, :genre, :logo, :website, :createdAt, keyword_init: true)
179
- RadioViewBasic = Struct.new(:id, :name, :description, :createdAt, keyword_init: true)
180
- RadioViewDetailed = Struct.new(:id, :name, :description, :website, :url, :genre, :logo, :createdAt, keyword_init: true)
181
- RemoveApikeyParams = Struct.new(:id, keyword_init: true)
182
- RemovePlaylistParams = Struct.new(:uri, keyword_init: true)
183
- RemoveShoutParams = Struct.new(:id, keyword_init: true)
184
- RemoveTrackParams = Struct.new(:uri, :position, keyword_init: true)
185
- ReplyShoutInput = Struct.new(:shoutId, :message, keyword_init: true)
186
- ReportShoutInput = Struct.new(:shoutId, :reason, keyword_init: true)
187
- ScrobbleFirstScrobbleView = Struct.new(:handle, :avatar, :timestamp, keyword_init: true)
188
- ScrobbleRecord = Struct.new(:title, :artist, :artists, :albumArtist, :album, :duration, :trackNumber, :discNumber, :releaseDate, :year, :genre, :tags, :composer, :lyrics, :copyrightMessage, :wiki, :albumArt, :albumArtUrl, :youtubeLink, :spotifyLink, :tidalLink, :appleMusicLink, :createdAt, :mbid, :label, :isrc, keyword_init: true)
189
- ScrobbleViewBasic = Struct.new(:id, :user, :userDisplayName, :userAvatar, :title, :artist, :artistUri, :album, :albumUri, :cover, :date, :uri, :sha256, :liked, :likesCount, keyword_init: true)
190
- ScrobbleViewDetailed = Struct.new(:id, :user, :title, :artist, :artistUri, :album, :albumUri, :cover, :date, :uri, :sha256, :liked, :likesCount, :listeners, :scrobbles, :artists, :firstScrobble, keyword_init: true)
191
- SearchParams = Struct.new(:query, keyword_init: true)
192
- SeekParams = Struct.new(:playerId, :position, keyword_init: true)
193
- ShoutAuthor = Struct.new(:id, :did, :handle, :displayName, :avatar, keyword_init: true)
194
- ShoutRecord = Struct.new(:message, :createdAt, :parent, :subject, keyword_init: true)
195
- ShoutView = Struct.new(:id, :message, :parent, :createdAt, :author, keyword_init: true)
196
- SongFirstScrobbleView = Struct.new(:handle, :avatar, :timestamp, keyword_init: true)
197
- SongRecentListenerView = Struct.new(:id, :did, :handle, :displayName, :avatar, :timestamp, :scrobbleUri, keyword_init: true)
198
- SongRecord = Struct.new(:title, :artist, :artists, :albumArtist, :album, :duration, :trackNumber, :discNumber, :releaseDate, :year, :genre, :tags, :composer, :lyrics, :copyrightMessage, :wiki, :albumArt, :albumArtUrl, :youtubeLink, :spotifyLink, :tidalLink, :appleMusicLink, :createdAt, :mbid, :label, :isrc, keyword_init: true)
199
- SongViewBasic = Struct.new(:id, :title, :artist, :albumArtist, :albumArt, :uri, :album, :duration, :trackNumber, :discNumber, :playCount, :uniqueListeners, :albumUri, :artistUri, :sha256, :mbid, :isrc, :tags, :createdAt, keyword_init: true)
200
- SongViewDetailed = Struct.new(:id, :title, :artist, :albumArtist, :albumArt, :uri, :album, :duration, :trackNumber, :discNumber, :playCount, :uniqueListeners, :albumUri, :artistUri, :sha256, :mbid, :isrc, :tags, :createdAt, :artists, :firstScrobble, keyword_init: true)
201
- SpotifyTrackView = Struct.new(:id, :name, :artist, :album, :duration, :previewUrl, keyword_init: true)
202
- StartPlaylistParams = Struct.new(:uri, :shuffle, :position, keyword_init: true)
203
- StatsGlobalStatsView = Struct.new(:scrobbles, :users, :artists, :albums, :tracks, keyword_init: true)
204
- StatsView = Struct.new(:scrobbles, :artists, :lovedTracks, :albums, :tracks, keyword_init: true)
205
- StatsWrappedAlbum = Struct.new(:id, :title, :artist, :albumArt, :uri, :playCount, keyword_init: true)
206
- StatsWrappedArtist = Struct.new(:id, :name, :picture, :uri, :playCount, keyword_init: true)
207
- StatsWrappedDayCount = Struct.new(:date, :count, keyword_init: true)
208
- StatsWrappedGenreCount = Struct.new(:genre, :count, keyword_init: true)
209
- StatsWrappedMilestone = Struct.new(:trackTitle, :artistName, :timestamp, :trackUri, keyword_init: true)
210
- StatsWrappedMonthCount = Struct.new(:month, :count, keyword_init: true)
211
- StatsWrappedTrack = Struct.new(:id, :title, :artist, :albumArt, :uri, :artistUri, :albumUri, :playCount, keyword_init: true)
212
- StatsWrappedView = Struct.new(:year, :totalScrobbles, :totalListeningTimeMinutes, :topArtists, :topTracks, :topAlbums, :topGenres, :scrobblesPerMonth, :mostActiveDay, :mostActiveHour, :newArtistsCount, :longestStreak, :firstScrobble, :lastScrobble, keyword_init: true)
213
- StatusRecord = Struct.new(:track, :startedAt, :expiresAt, keyword_init: true)
214
- StrongRef = Struct.new(:uri, :cid, keyword_init: true)
215
- UnfollowAccountOutput = Struct.new(:subject, :followers, :cursor, keyword_init: true)
216
- UnfollowAccountParams = Struct.new(:account, keyword_init: true)
217
- UpdateApikeyInput = Struct.new(:id, :name, :description, keyword_init: true)
218
- end
219
- end
data/lib/rocksky/http.rb DELETED
@@ -1,123 +0,0 @@
1
- require "json"
2
- require "net/http"
3
- require "uri"
4
-
5
- module Rocksky
6
- # Low-level XRPC transport. Most users should go through the resource
7
- # accessors on {Rocksky::Client} (e.g. `client.actor.get_profile(...)`).
8
- class HTTP
9
- DEFAULT_OPEN_TIMEOUT = 10
10
- DEFAULT_READ_TIMEOUT = 30
11
-
12
- attr_reader :client
13
-
14
- def initialize(client)
15
- @client = client
16
- end
17
-
18
- # GET /xrpc/<nsid>?...
19
- def query(nsid, params = {})
20
- request(method: :get, nsid: nsid, params: params)
21
- end
22
-
23
- # POST /xrpc/<nsid>?... with optional JSON body.
24
- def procedure(nsid, params = {}, body = nil)
25
- request(method: :post, nsid: nsid, params: params, body: body)
26
- end
27
-
28
- private
29
-
30
- def request(method:, nsid:, params: {}, body: nil)
31
- uri = build_uri(nsid, params)
32
- req = build_request(method, uri, body)
33
- apply_headers(req)
34
-
35
- response = perform(uri, req)
36
- handle_response(response, nsid)
37
- rescue Timeout::Error, Errno::ECONNREFUSED, SocketError, IOError,
38
- Net::OpenTimeout, Net::ReadTimeout => e
39
- raise Error.from_transport(e, nsid: nsid)
40
- end
41
-
42
- def build_uri(nsid, params)
43
- url = "#{client.base_url}/xrpc/#{nsid}"
44
- uri = URI.parse(url)
45
- encoded = encode_params(params)
46
- uri.query = URI.encode_www_form(encoded) unless encoded.empty?
47
- uri
48
- end
49
-
50
- def build_request(method, uri, body)
51
- req =
52
- case method
53
- when :get then Net::HTTP::Get.new(uri.request_uri)
54
- when :post then Net::HTTP::Post.new(uri.request_uri)
55
- else raise ArgumentError, "unsupported method: #{method}"
56
- end
57
-
58
- if body && !body.empty?
59
- req["content-type"] = "application/json"
60
- req.body = JSON.generate(body)
61
- end
62
-
63
- req
64
- end
65
-
66
- def apply_headers(req)
67
- req["accept"] = "application/json"
68
- req["user-agent"] = client.user_agent if client.user_agent
69
- req["authorization"] = "Bearer #{client.token}" if client.token
70
- client.headers.each { |name, value| req[name] = value }
71
- end
72
-
73
- def perform(uri, req)
74
- http = Net::HTTP.new(uri.host, uri.port)
75
- http.use_ssl = (uri.scheme == "https")
76
- http.open_timeout = client.open_timeout
77
- http.read_timeout = client.read_timeout
78
- http.request(req)
79
- end
80
-
81
- def handle_response(response, nsid)
82
- status = response.code.to_i
83
- body = parse_body(response)
84
-
85
- return body if status.between?(200, 299)
86
-
87
- raise Error.from_response(status, body, nsid: nsid)
88
- end
89
-
90
- def parse_body(response)
91
- return nil if response.body.nil? || response.body.empty?
92
-
93
- content_type = response["content-type"].to_s
94
- if content_type.include?("application/json")
95
- JSON.parse(response.body)
96
- else
97
- response.body
98
- end
99
- rescue JSON::ParserError
100
- response.body
101
- end
102
-
103
- # Drop nil values; convert symbol keys to strings; join arrays with commas
104
- # (matches the lexicon-defined array encoding used by other SDKs).
105
- def encode_params(params)
106
- return {} if params.nil?
107
-
108
- Hash(params).each_with_object({}) do |(key, value), out|
109
- next if value.nil?
110
-
111
- out[key.to_s] = encode_value(value)
112
- end
113
- end
114
-
115
- def encode_value(value)
116
- case value
117
- when Array then value.join(",")
118
- when true, false then value.to_s
119
- else value
120
- end
121
- end
122
- end
123
- end
@@ -1,60 +0,0 @@
1
- module Rocksky
2
- module Resources
3
- # `app.rocksky.actor.*` endpoints.
4
- class Actor < Base
5
- # Fetch a profile by DID or handle.
6
- def get_profile(did:)
7
- query("app.rocksky.actor.getProfile", did: did)
8
- end
9
-
10
- # Albums an actor has scrobbled.
11
- def get_actor_albums(did:, limit: nil, offset: nil, start_date: nil, end_date: nil)
12
- query("app.rocksky.actor.getActorAlbums",
13
- did: did, limit: limit, offset: offset,
14
- startDate: start_date, endDate: end_date)
15
- end
16
-
17
- # Artists an actor has scrobbled.
18
- def get_actor_artists(did:, limit: nil, offset: nil, start_date: nil, end_date: nil)
19
- query("app.rocksky.actor.getActorArtists",
20
- did: did, limit: limit, offset: offset,
21
- startDate: start_date, endDate: end_date)
22
- end
23
-
24
- # Songs an actor has scrobbled.
25
- def get_actor_songs(did:, limit: nil, offset: nil, start_date: nil, end_date: nil)
26
- query("app.rocksky.actor.getActorSongs",
27
- did: did, limit: limit, offset: offset,
28
- startDate: start_date, endDate: end_date)
29
- end
30
-
31
- # Songs an actor has loved.
32
- def get_actor_loved_songs(did:, limit: nil, offset: nil)
33
- query("app.rocksky.actor.getActorLovedSongs",
34
- did: did, limit: limit, offset: offset)
35
- end
36
-
37
- # Scrobbles for an actor.
38
- def get_actor_scrobbles(did:, limit: nil, offset: nil)
39
- query("app.rocksky.actor.getActorScrobbles",
40
- did: did, limit: limit, offset: offset)
41
- end
42
-
43
- # Playlists for an actor.
44
- def get_actor_playlists(did:, limit: nil, offset: nil)
45
- query("app.rocksky.actor.getActorPlaylists",
46
- did: did, limit: limit, offset: offset)
47
- end
48
-
49
- # Musical neighbours of an actor.
50
- def get_actor_neighbours(did:)
51
- query("app.rocksky.actor.getActorNeighbours", did: did)
52
- end
53
-
54
- # Compatibility score between the authenticated user and another actor.
55
- def get_actor_compatibility(did:)
56
- query("app.rocksky.actor.getActorCompatibility", did: did)
57
- end
58
- end
59
- end
60
- end
@@ -1,22 +0,0 @@
1
- module Rocksky
2
- module Resources
3
- # `app.rocksky.album.*` endpoints.
4
- class Album < Base
5
- # Fetch an album by AT-URI.
6
- def get_album(uri:)
7
- query("app.rocksky.album.getAlbum", uri: uri)
8
- end
9
-
10
- # List albums.
11
- def get_albums(limit: nil, offset: nil, genre: nil)
12
- query("app.rocksky.album.getAlbums",
13
- limit: limit, offset: offset, genre: genre)
14
- end
15
-
16
- # Tracks belonging to an album.
17
- def get_album_tracks(uri:)
18
- query("app.rocksky.album.getAlbumTracks", uri: uri)
19
- end
20
- end
21
- end
22
- end
@@ -1,28 +0,0 @@
1
- module Rocksky
2
- module Resources
3
- # `app.rocksky.apikey.*` endpoints. All require an authenticated client.
4
- class Apikey < Base
5
- # List your API keys.
6
- def get_apikeys(limit: nil, offset: nil)
7
- query("app.rocksky.apikey.getApikeys", limit: limit, offset: offset)
8
- end
9
-
10
- # Create a new API key.
11
- def create_apikey(name:, description: nil)
12
- body = { name: name, description: description }.compact
13
- procedure("app.rocksky.apikey.createApikey", body: body)
14
- end
15
-
16
- # Update an API key.
17
- def update_apikey(id:, name: nil, description: nil)
18
- body = { id: id, name: name, description: description }.compact
19
- procedure("app.rocksky.apikey.updateApikey", body: body)
20
- end
21
-
22
- # Remove an API key.
23
- def remove_apikey(id:)
24
- procedure("app.rocksky.apikey.removeApikey", params: { id: id })
25
- end
26
- end
27
- end
28
- end
@@ -1,40 +0,0 @@
1
- module Rocksky
2
- module Resources
3
- # `app.rocksky.artist.*` endpoints.
4
- class Artist < Base
5
- # Fetch an artist by AT-URI.
6
- def get_artist(uri:)
7
- query("app.rocksky.artist.getArtist", uri: uri)
8
- end
9
-
10
- # List artists. `names` may be an Array of strings; it will be joined CSV-style.
11
- def get_artists(limit: nil, offset: nil, names: nil, genre: nil)
12
- query("app.rocksky.artist.getArtists",
13
- limit: limit, offset: offset, names: names, genre: genre)
14
- end
15
-
16
- # Albums by an artist.
17
- def get_artist_albums(uri:)
18
- query("app.rocksky.artist.getArtistAlbums", uri: uri)
19
- end
20
-
21
- # Tracks by an artist.
22
- def get_artist_tracks(uri:, limit: nil, offset: nil)
23
- query("app.rocksky.artist.getArtistTracks",
24
- uri: uri, limit: limit, offset: offset)
25
- end
26
-
27
- # All-time listeners for an artist.
28
- def get_artist_listeners(uri:, limit: nil, offset: nil)
29
- query("app.rocksky.artist.getArtistListeners",
30
- uri: uri, limit: limit, offset: offset)
31
- end
32
-
33
- # Recent listeners for an artist.
34
- def get_artist_recent_listeners(uri:, limit: nil, offset: nil)
35
- query("app.rocksky.artist.getArtistRecentListeners",
36
- uri: uri, limit: limit, offset: offset)
37
- end
38
- end
39
- end
40
- end
@@ -1,24 +0,0 @@
1
- module Rocksky
2
- module Resources
3
- # Shared plumbing for resource classes. Each subclass calls {#query} or
4
- # {#procedure} with the lexicon NSID; the {Rocksky::HTTP} instance does the
5
- # actual transport work.
6
- class Base
7
- attr_reader :http
8
-
9
- def initialize(http)
10
- @http = http
11
- end
12
-
13
- protected
14
-
15
- def query(nsid, **params)
16
- @http.query(nsid, params)
17
- end
18
-
19
- def procedure(nsid, params: {}, body: nil)
20
- @http.procedure(nsid, params, body)
21
- end
22
- end
23
- end
24
- end