Class: Rockbox::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/rockbox/client.rb

Overview


Rockbox::Client — main entry point.

Inspired by:

Mopidy    — domain namespace API (client.playback.play, client.library.search)
Jellyfin  — plugin install/uninstall lifecycle
Kodi      — rich device + playlist management

Examples:

Quick start

client = Rockbox::Client.new
client.connect  # start WebSocket subscriptions

client.on(:track_changed) { |track| puts "Now playing: #{track.title}" }

results = client.library.search("dark side")
client.playback.play_album(results.albums.first.id, shuffle: true)

Builder DSL

client = Rockbox::Client.build do |c|
  c.host = "192.168.1.42"
  c.port = 6062
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host: nil, port: nil, http_url: nil, ws_url: nil, open_timeout: nil, read_timeout: nil, configuration: nil) ⇒ Client

Returns a new instance of Client.

Parameters:

  • host (String) (defaults to: nil)

    hostname or IP of rockboxd (default: “localhost”)

  • port (Integer) (defaults to: nil)

    GraphQL port (default: 6062)

  • http_url (String) (defaults to: nil)

    override the full HTTP URL

  • ws_url (String) (defaults to: nil)

    override the full WebSocket URL

  • open_timeout (Integer) (defaults to: nil)

    HTTP connect timeout (seconds)

  • read_timeout (Integer) (defaults to: nil)

    HTTP read timeout (seconds)

  • configuration (Configuration) (defaults to: nil)

    pre-built configuration (rare)



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
# File 'lib/rockbox/client.rb', line 64

def initialize(host: nil, port: nil, http_url: nil, ws_url: nil,
               open_timeout: nil, read_timeout: nil, configuration: nil)
  @configuration = configuration || Configuration.new(
    host: host, port: port,
    http_url: http_url, ws_url: ws_url,
    open_timeout: open_timeout, read_timeout: read_timeout
  )

  @http = HttpTransport.new(
    @configuration.resolved_http_url,
    open_timeout: @configuration.open_timeout || HttpTransport::DEFAULT_OPEN_TIMEOUT,
    read_timeout: @configuration.read_timeout || HttpTransport::DEFAULT_READ_TIMEOUT
  )
  @ws = WsTransport.new(@configuration.resolved_ws_url)

  @events = EventEmitter.new
  @plugins = PluginRegistry.new
  @subscriptions = []

  @playback        = Api::Playback.new(@http)
  @library         = Api::Library.new(@http)
  @playlist        = Api::Playlist.new(@http)
  @saved_playlists = Api::SavedPlaylists.new(@http)
  @smart_playlists = Api::SmartPlaylists.new(@http)
  @sound           = Api::Sound.new(@http)
  @settings        = Api::Settings.new(@http)
  @system          = Api::System.new(@http)
  @browse          = Api::Browse.new(@http)
  @devices         = Api::Devices.new(@http)
  @bluetooth       = Api::Bluetooth.new(@http)
end

Instance Attribute Details

#bluetoothObject (readonly)

Returns the value of attribute bluetooth.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def bluetooth
  @bluetooth
end

#browseObject (readonly)

Returns the value of attribute browse.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def browse
  @browse
end

#configurationObject (readonly)

Returns the value of attribute configuration.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def configuration
  @configuration
end

#devicesObject (readonly)

Returns the value of attribute devices.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def devices
  @devices
end

#libraryObject (readonly)

Returns the value of attribute library.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def library
  @library
end

#playbackObject (readonly)

Returns the value of attribute playback.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def playback
  @playback
end

#playlistObject (readonly)

Returns the value of attribute playlist.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def playlist
  @playlist
end

#saved_playlistsObject (readonly)

Returns the value of attribute saved_playlists.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def saved_playlists
  @saved_playlists
end

#settingsObject (readonly)

Returns the value of attribute settings.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def settings
  @settings
end

#smart_playlistsObject (readonly)

Returns the value of attribute smart_playlists.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def smart_playlists
  @smart_playlists
end

#soundObject (readonly)

Returns the value of attribute sound.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def sound
  @sound
end

#systemObject (readonly)

Returns the value of attribute system.



46
47
48
# File 'lib/rockbox/client.rb', line 46

def system
  @system
end

Class Method Details

.build {|config| ... } ⇒ Object

Block-form constructor — yields a Rockbox::Configuration for tweaking.

Yields:

  • (config)


51
52
53
54
55
# File 'lib/rockbox/client.rb', line 51

def self.build
  config = Configuration.new
  yield config if block_given?
  new(configuration: config)
end

Instance Method Details

#connectself

Open the WebSocket and subscribe to the three default streams. Idempotent.

Returns:

  • (self)


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
# File 'lib/rockbox/client.rb', line 113

def connect
  return self unless @subscriptions.empty?

  @subscriptions << @ws.subscribe(
    <<~GQL, nil,
      subscription CurrentlyPlaying {
        currentlyPlayingSong {
          id title artist album albumArt albumId artistId path length elapsed
        }
      }
    GQL
    next: ->(result) {
      payload = result[:data]&.dig(:currently_playing_song)
      @events.emit(:track_changed, Track.from_hash(payload)) if payload
    },
    error: ->(err) { @events.emit(:ws_error, wrap_error(err)) },
    complete: -> {}
  )

  @subscriptions << @ws.subscribe(
    "subscription PlaybackStatus { playbackStatus { status } }", nil,
    next: ->(result) {
      status = result[:data]&.dig(:playback_status, :status)
      @events.emit(:status_changed, status) unless status.nil?
    },
    error: ->(err) { @events.emit(:ws_error, wrap_error(err)) },
    complete: -> {}
  )

  @subscriptions << @ws.subscribe(
    <<~GQL, nil,
      subscription PlaylistChanged {
        playlistChanged {
          amount index maxPlaylistSize firstIndex lastInsertPos seed lastShuffledStart
          tracks { id title artist album path length albumArt }
        }
      }
    GQL
    next: ->(result) {
      payload = result[:data]&.dig(:playlist_changed)
      if payload
        tracks = Array(payload[:tracks]).map { |t| Track.from_hash(t) }
        playlist = Rockbox::Playlist.new(
          amount:              payload[:amount],
          index:               payload[:index],
          max_playlist_size:   payload[:max_playlist_size],
          first_index:         payload[:first_index],
          last_insert_pos:     payload[:last_insert_pos],
          seed:                payload[:seed],
          last_shuffled_start: payload[:last_shuffled_start],
          tracks:              tracks
        )
        @events.emit(:playlist_changed, playlist)
      end
    },
    error: ->(err) { @events.emit(:ws_error, wrap_error(err)) },
    complete: -> {}
  )

  @events.emit(:ws_open)
  self
end

#disconnectObject

Tear down subscriptions and close the WebSocket.



177
178
179
180
181
182
183
# File 'lib/rockbox/client.rb', line 177

def disconnect
  @subscriptions.each { |unsub| unsub.call rescue nil }
  @subscriptions.clear
  @ws.dispose
  @events.emit(:ws_close)
  self
end

#emit(event, payload = nil) ⇒ Object



103
# File 'lib/rockbox/client.rb', line 103

def emit(event, payload = nil);            @events.emit(event, payload);  self; end

#installed_pluginsObject



205
206
207
# File 'lib/rockbox/client.rb', line 205

def installed_plugins
  @plugins.list
end

#off(event, listener = nil, &block) ⇒ Object



102
# File 'lib/rockbox/client.rb', line 102

def off(event, listener = nil, &block);    @events.off(event, listener, &block); self; end

#on(event, &block) ⇒ Object


Events — block-friendly delegation to the EventEmitter




100
# File 'lib/rockbox/client.rb', line 100

def on(event, &block);                     @events.on(event, &block);  self; end

#once(event, &block) ⇒ Object



101
# File 'lib/rockbox/client.rb', line 101

def once(event, &block);                   @events.once(event, &block); self; end

#query(query, variables = nil) ⇒ Hash

Returns the snake-cased data object.

Parameters:

  • query (String)
  • variables (Hash, nil) (defaults to: nil)

Returns:

  • (Hash)

    the snake-cased data object



216
217
218
# File 'lib/rockbox/client.rb', line 216

def query(query, variables = nil)
  @http.execute(query, variables)
end

#remove_all_listeners(event = nil) ⇒ Object



104
# File 'lib/rockbox/client.rb', line 104

def remove_all_listeners(event = nil);     @events.remove_all_listeners(event); self; end

#unuse(name) ⇒ Object



200
201
202
203
# File 'lib/rockbox/client.rb', line 200

def unuse(name)
  @plugins.unregister(name)
  self
end

#use(plugin) ⇒ Object

Examples:

client.use(MyScrobbler.new(api_key: "..."))


191
192
193
194
195
196
197
198
# File 'lib/rockbox/client.rb', line 191

def use(plugin)
  ctx = PluginContext.new(
    query: ->(gql, variables = nil) { @http.execute(gql, variables) },
    events: @events
  )
  @plugins.register(plugin, ctx)
  self
end