Class: Hallon::Session

Inherits:
Base
  • Object
show all
Extended by:
Observable::Session
Includes:
Singleton
Defined in:
lib/hallon/session.rb

Overview

The Session is fundamental for all communication with Spotify. Pretty much all API calls require you to have established a session with Spotify before using them.

Instance Attribute Summary collapse

Attributes inherited from Base

#pointer

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Observable::Session

connection_error_callback, connectionstate_updated_callback, credentials_blob_updated_callback, end_of_track_callback, extended, get_audio_buffer_stats_callback, initialize_callbacks, log_message_callback, logged_in_callback, logged_out_callback, message_to_user_callback, metadata_updated_callback, music_delivery_callback, notify_main_thread_callback, offline_error_callback, offline_status_updated_callback, play_token_lost_callback, private_session_mode_changed_callback, scrobble_error_callback, start_playback_callback, stop_playback_callback, streaming_error_callback, userinfo_updated_callback

Methods inherited from Base

#==, from, from_link, #is_linkable?, #session, to_link, #to_pointer, #to_s

Constructor Details

#initialize(appkey, options = {}) { ... } ⇒ Session

Create a new Spotify session.

Examples:

using a https-proxy

session = Hallon::Session.initialize(appkey, proxy: 'https://user@password:hostname')

Parameters:

  • appkey (#to_s)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :user_agent (String) — default: "Hallon"

    User-Agent to use (length < 256)

  • :settings_location (String) — default: "tmp"

    where to save settings and user-specific cache

  • :cache_location (String) — default: "") where to save cache files (`""` to disable
  • :tracefile (String) — default: nil

    path to libspotify API tracefile (nil to disable)

  • :device_id (String) — default: nil

    device ID for offline synchronization (nil to disable)

  • :proxy (String) — default: nil

    proxy URI (supports http, https, socks4, socks5)

  • :load_playlists (Bool) — default: true

    load playlists into RAM on startup

  • :compress_playlists (Bool) — default: true

    compress local copies of playlists

  • :cache_playlist_metadata (Bool) — default: true

    cache metadata for playlists locally

Yields:

Raises:

  • (ArgumentError)

    if options[:user_agent] is more than 256 characters long

  • (Hallon::Error)

    if sp_session_create fails

See Also:



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
# File 'lib/hallon/session.rb', line 97

def initialize(appkey, options = {}, &block)
  if options[:proxy]
    proxy_uri = URI(options[:proxy])
    options[:proxy_username] ||= proxy_uri.user
    options[:proxy_password] ||= proxy_uri.password
    proxy_uri.user = proxy_uri.password = nil
    options[:proxy] = proxy_uri.to_s
  end

  @options = {
    :user_agent => "Hallon",
    :settings_location => "tmp/hallon/",
    :cache_location => "tmp/hallon/",
    :load_playlists => true,
    :compress_playlists => true,
    :cache_playlist_metadata => true,
    :device_id => nil,
    :proxy     => nil,
    :proxy_username => nil,
    :proxy_password => nil,
    :tracefile => nil,
  }.merge(options)

  if @options[:user_agent].bytesize > 255
    raise ArgumentError, "User-agent must be less than 256 bytes long"
  end

  # Default cache size is 0 (automatic)
  @cache_size = 0

  subscribe_for_callbacks do |callbacks|
    # not overridable
    @options[:api_version] = Hallon::API_VERSION
    @options[:initially_unload_playlists] = ! @options.delete(:load_playlists)
    @options[:dont_save_metadata_for_playlists] = ! @options.delete(:cache_playlist_metadata)

    config = Spotify::SessionConfig.new(@options.merge(application_key: appkey, callbacks: callbacks))

    instance_eval(&block) if block_given?

    # You pass a pointer to the session pointer to libspotify >:)
    FFI::MemoryPointer.new(:pointer) do |p|
      Error::maybe_raise Spotify.session_create(config, p)
      @pointer = Spotify::Session.new(p.read_pointer)
    end
  end
end

Instance Attribute Details

#cache_sizeInteger

Note:

This is not provided by libspotify, and the value is only valid as long as the cache size is only adjusted through #cache_size= and not the Spotify FFI interface.

The current session cache size (in megabytes).

Returns:

  • (Integer)


26
27
28
# File 'lib/hallon/session.rb', line 26

def cache_size
  @cache_size
end

#optionsHash (readonly)

The options Hallon used at #initialize.

Returns:

  • (Hash)


17
18
19
# File 'lib/hallon/session.rb', line 17

def options
  @options
end

Class Method Details

.connection_rulesArray<Symbol>

Returns list of available connection rules.

Returns:

  • (Array<Symbol>)

    list of available connection rules.



73
74
75
# File 'lib/hallon/session.rb', line 73

def self.connection_rules
  Spotify.enum_type(:connection_rules).symbols
end

.connection_typesArray<Symbol>

Returns list of available connection types.

Returns:

  • (Array<Symbol>)

    list of available connection types.



68
69
70
# File 'lib/hallon/session.rb', line 68

def self.connection_types
  Spotify.enum_type(:connection_type).symbols
end

.initialize(appkey, options = {}) { ... } ⇒ Session

Initializes the Spotify session. If you need to access the instance at a later time, you can use instance.

Parameters:

  • appkey (#to_s)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :user_agent (String) — default: "Hallon"

    User-Agent to use (length < 256)

  • :settings_location (String) — default: "tmp"

    where to save settings and user-specific cache

  • :cache_location (String) — default: "") where to save cache files (`""` to disable
  • :tracefile (String) — default: nil

    path to libspotify API tracefile (nil to disable)

  • :device_id (String) — default: nil

    device ID for offline synchronization (nil to disable)

  • :proxy (String) — default: nil

    proxy URI (supports http, https, socks4, socks5)

  • :load_playlists (Bool) — default: true

    load playlists into RAM on startup

  • :compress_playlists (Bool) — default: true

    compress local copies of playlists

  • :cache_playlist_metadata (Bool) — default: true

    cache metadata for playlists locally

Yields:

Returns:

Raises:

  • (ArgumentError)

    if options[:user_agent] is more than 256 characters long

  • (Hallon::Error)

    if sp_session_create fails

See Also:



48
49
50
51
# File 'lib/hallon/session.rb', line 48

def Session.initialize(appkey, options = {}, &block)
  raise "Session has already been initialized" if defined?(@__instance__)
  @__instance__ = new(appkey, options, &block)
end

.instanceSession

Returns the previously initialized Session.

Returns:

See Also:



58
59
60
# File 'lib/hallon/session.rb', line 58

def Session.instance
  @__instance__ or raise NoSessionError, "Session has not been initialized"
end

.instance?Boolean

Returns true if a Session instance exists.

Returns:

  • (Boolean)

    true if a Session instance exists.



63
64
65
# File 'lib/hallon/session.rb', line 63

def Session.instance?
  !! @__instance__
end

Instance Method Details

#connection_rules=(connection_rules) ⇒ Object

Set the connection rules for this session.

Parameters:

  • connection_rules (Symbol, )

Raises:

  • (ArgumentError)

    if given invalid connection rules

See Also:



348
349
350
351
352
353
354
# File 'lib/hallon/session.rb', line 348

def connection_rules=(connection_rules)
  rules = Array(connection_rules).reduce(0) do |mask, rule|
    mask | Spotify.enum_value!(rule, "connection rule")
  end

  Spotify.session_set_connection_rules(pointer, rules)
end

#connection_type=(connection_type) ⇒ Object

Set the connection type for this session.

Parameters:

  • connection_type (Symbol)

Raises:

  • (ArgumentError)

    if given invalid connection type

See Also:



361
362
363
# File 'lib/hallon/session.rb', line 361

def connection_type=(connection_type)
  Spotify.session_set_connection_type(pointer, connection_type)
end

#containerPlaylistContainer?

Note:

returns nil if the session is not logged in.

PlaylistContainer for the currently logged in session.

Returns:



158
159
160
161
# File 'lib/hallon/session.rb', line 158

def container
  container = Spotify.session_playlistcontainer(pointer)
  PlaylistContainer.from(container)
end

#countryString

Returns currently logged in user’s country.

Returns:

  • (String)

    currently logged in user’s country.



304
305
306
307
308
# File 'lib/hallon/session.rb', line 304

def country
  coded = Spotify.session_user_country(pointer)
  country = ((coded >> 8) & 0xFF).chr
  country << (coded & 0xFF).chr
end

#disconnected?Boolean

Returns true if session has been disconnected.

Returns:

  • (Boolean)

    true if session has been disconnected.

See Also:



446
447
448
# File 'lib/hallon/session.rb', line 446

def disconnected?
  status == :disconnected
end

#flush_cachesSession

Note:

libspotify does this automatically periodically, under normal circumstances this method should not need to be used.

Flushes the Session cache to disk.

Returns:



150
151
152
# File 'lib/hallon/session.rb', line 150

def flush_caches
  Spotify.session_flush_caches(pointer)
end

#forget_me!self

Note:

If no credentials are stored nothing’ll happen.

Remove stored login credentials in libspotify.

Returns:

  • (self)


258
259
260
# File 'lib/hallon/session.rb', line 258

def forget_me!
  tap { Spotify.session_forget_me(pointer) }
end

#inboxPlaylist?

Note:

Returns nil when no user is logged in.

Returns currently logged in user’s inbox playlist.

Returns:

  • (Playlist, nil)

    currently logged in user’s inbox playlist.



427
428
429
430
# File 'lib/hallon/session.rb', line 427

def inbox
  playlist = Spotify.session_inbox_create(pointer)
  Playlist.from(playlist)
end

#logged_in?Boolean

Returns true if logged in.

Returns:

  • (Boolean)

    true if logged in.

See Also:



434
435
436
# File 'lib/hallon/session.rb', line 434

def logged_in?
  status == :logged_in
end

#logged_out?Boolean

Returns true if logged out.

Returns:

  • (Boolean)

    true if logged out.

See Also:



440
441
442
# File 'lib/hallon/session.rb', line 440

def logged_out?
  status == :logged_out
end

#login(username, password, remember_me = false) ⇒ Session

Note:

it also supports logging in via a credentials blob, if you pass a Hallon::Blob(blob_string) as the password instead of the real password

Log into Spotify using the given credentials.

Examples:

logging in with password

session. 'Kim', 'password'

logging in with credentials blob

session. 'Kim', Hallon::Blob('blob string')

Parameters:

  • username (String)
  • password (String)

    your password, or user credentials blob

  • remember_me (Boolean) (defaults to: false)

    have libspotify remember credentials for #relogin

Returns:

See Also:



188
189
190
191
192
193
194
195
# File 'lib/hallon/session.rb', line 188

def (username, password, remember_me = false)
  if username.empty? or password.empty?
    raise ArgumentError, "username and password may not be blank"
  end

  password, blob = blob, password if password.is_a?(Blob)
  tap { Spotify.(pointer, username, password, remember_me, blob) }
end

#login!(username, password, remember_me = false) ⇒ Session

Note:

This function will not return until you’ve either logged in successfully, or until an error is raised.

Log in to Spotify using the given credentials.

Parameters:

  • username (String)
  • password (String)

    your password, or user credentials blob

  • remember_me (Boolean) (defaults to: false)

    have libspotify remember credentials for #relogin

Returns:

Raises:

  • (Error)

    if failed to log in

See Also:



213
214
215
216
# File 'lib/hallon/session.rb', line 213

def login!(username, password, remember_me = false)
  (username, password, remember_me)
  tap { wait_until_logged_in }
end

#logoutself

Logs out of Spotify. Does nothing if not logged in.

Returns:

  • (self)


265
266
267
# File 'lib/hallon/session.rb', line 265

def logout
  tap { Spotify.session_logout(pointer) if logged_in? }
end

#logout!Session

Note:

This method will not return until you’ve logged out successfully.

Log out the current user.

Returns:



234
235
236
237
# File 'lib/hallon/session.rb', line 234

def logout!
  logout
  tap { wait_for(:logged_out) { logged_out? } }
end

#offline?Boolean

Returns true if offline.

Returns:

  • (Boolean)

    true if offline.

See Also:



452
453
454
# File 'lib/hallon/session.rb', line 452

def offline?
  status == :offline
end

#offline_bitrate=(bitrate) ⇒ Object

Note:

under normal circumstances, ArgumentError is the error that will be raised on an invalid bitrate. However, if Hallon fails the type checking (for whatever reason), libspotify will itself return an error as well.

Set preferred offline bitrate.

Examples:

setting offline bitrate without resync

session.offline_bitrate = :'96k'

setting offline bitrate and resync already-synced tracks

session.offline_bitrate = :'96k', true

Parameters:

  • bitrate (Symbol)
  • resync (Boolean)

    (default: false)

Raises:

  • (ArgumentError)

    if given invalid bitrate

  • (Spotify::Error)

    if libspotify does not accept the given bitrate (see note)

See Also:



413
414
415
416
# File 'lib/hallon/session.rb', line 413

def offline_bitrate=(bitrate)
  bitrate, resync = Array(bitrate)
  Spotify.try(:session_preferred_offline_bitrate, pointer, bitrate, !! resync)
end

#offline_playlists_countInteger

Returns number of playlists marked for offline sync.

Returns:

  • (Integer)

    number of playlists marked for offline sync.



386
387
388
# File 'lib/hallon/session.rb', line 386

def offline_playlists_count
  Spotify.offline_num_playlists(pointer)
end

#offline_sync_statusHash

Offline synchronization status.

Returns:

  • (Hash)

    sync status (empty hash if not applicable)

See Also:



376
377
378
379
380
381
382
383
# File 'lib/hallon/session.rb', line 376

def offline_sync_status
  struct = Spotify::OfflineSyncStatus.new
  if Spotify.offline_sync_get_status(pointer, struct)
    struct.to_h
  else
    {}
  end
end

#offline_time_leftInteger

Remaining time left you can stay offline before needing to relogin.

Returns:

  • (Integer)

    offline time left in seconds



368
369
370
# File 'lib/hallon/session.rb', line 368

def offline_time_left
  Spotify.offline_time_left(pointer)
end

#offline_tracks_to_syncInteger

Returns number of offline tracks left to sync for offline mode.

Returns:

  • (Integer)

    number of offline tracks left to sync for offline mode.



391
392
393
# File 'lib/hallon/session.rb', line 391

def offline_tracks_to_sync
  Spotify.offline_tracks_to_sync(pointer)
end

#private=(is_private) ⇒ Object Also known as: britney_spears_mode=

Note:

mode is reverted to normal after some time without user activity, see official libspotify documentation for details.

Set private session.

Parameters:

  • is_private (Boolean)


291
292
293
# File 'lib/hallon/session.rb', line 291

def private=(is_private)
  Spotify.session_set_private_session(pointer, !! is_private)
end

#private?Boolean Also known as: britney_spears_mode?

Returns true if the session is currently set to private.

Returns:

  • (Boolean)

    true if the session is currently set to private.



281
282
283
# File 'lib/hallon/session.rb', line 281

def private?
  Spotify.session_is_private_session(pointer)
end

#process_eventsFixnum

Process pending Spotify events (might fire callbacks).

Returns:

  • (Fixnum)

    time (in milliseconds) until it should be called again



166
167
168
169
170
171
# File 'lib/hallon/session.rb', line 166

def process_events
  FFI::MemoryPointer.new(:int) do |p|
    Spotify.session_process_events(pointer, p)
    return p.read_int
  end
end

#reloginObject

Login the remembered user (see #login).

Raises:

  • (Spotify::Error)

    if no credentials are stored in libspotify

See Also:



201
202
203
# File 'lib/hallon/session.rb', line 201

def relogin
  Spotify.try(:session_relogin, pointer)
end

#relogin!Session

Note:

This method will not return until you’ve either logged in successfully or until an error is raised.

Log in the remembered user.

Returns:

Raises:

  • (Error)

    if failed to log in

See Also:



225
226
227
228
# File 'lib/hallon/session.rb', line 225

def relogin!
  relogin
  tap { wait_until_logged_in }
end

#remembered_userString?

Returns username of the user stored in libspotify-remembered credentials.

Returns:

  • (String, nil)

    username of the user stored in libspotify-remembered credentials.



246
247
248
249
250
251
252
# File 'lib/hallon/session.rb', line 246

def remembered_user
  bufflen = Spotify.session_remembered_user(pointer, nil, 0)
  FFI::Buffer.alloc_out(bufflen + 1) do |b|
    Spotify.session_remembered_user(pointer, b, b.size)
    return b.get_string(0)
  end if bufflen > 0
end

#star(*tracks) ⇒ Session

Note:

this method might raise a Spotify::Error, however when this might occur is not documented in libspotify (and I have yet to find any way to trigger it myself). it’s entirely possible that this method never returns an error, but we can’t know for sure.

Star the given tracks.

Examples:

track = Hallon::Track.new("spotify:track:2LFQV2u6wXZmmySCWBkYGu")
session.star(track)

Parameters:

Returns:

Raises:

  • (Spotify:Error)

    if libspotify reports an error (when this happens is unknown and undocumented)



321
322
323
# File 'lib/hallon/session.rb', line 321

def star(*tracks)
  tap { tracks_starred(tracks, true) }
end

#starredPlaylist?

Note:

Returns nil when no user is logged in.

Returns currently logged in user’s starred playlist.

Returns:

  • (Playlist, nil)

    currently logged in user’s starred playlist.



420
421
422
423
# File 'lib/hallon/session.rb', line 420

def starred
  playlist = Spotify.session_starred_create(pointer)
  Playlist.from(playlist)
end

#statusSymbol

Returns current connection status.

Returns:

  • (Symbol)

    current connection status.



276
277
278
# File 'lib/hallon/session.rb', line 276

def status
  Spotify.session_connectionstate(pointer)
end

#tracks_starred(tracks, starred) ⇒ Object (private)

Set starred status of given tracks.

Parameters:

  • tracks (Array<Track>)
  • starred (Boolean)

Raises:

  • (Spotify::Error)

    … maybe, it’s undocumented in libspotify, who knows?



462
463
464
465
466
467
# File 'lib/hallon/session.rb', line 462

def tracks_starred(tracks, starred)
  FFI::MemoryPointer.new(:pointer, tracks.size) do |ptr|
    ptr.write_array_of_pointer tracks.map(&:pointer)
    Spotify.try(:track_set_starred, pointer, ptr, tracks.size, starred)
  end
end

#unstar(*tracks) ⇒ Session

Note:

this method might raise a Spotify::Error, however when this might occur is not documented in libspotify (and I have yet to find any way to trigger it myself). it’s entirely possible that this method never returns an error, but we can’t know for sure.

Unstar the given tracks.

Examples:

track = Hallon::Track.new("spotify:track:2LFQV2u6wXZmmySCWBkYGu")
session.unstar(track)

Parameters:

Returns:

Raises:

  • (Spotify:Error)

    if libspotify reports an error (when this happens is unknown and undocumented)



339
340
341
# File 'lib/hallon/session.rb', line 339

def unstar(*tracks)
  tap { tracks_starred(tracks, false) }
end

#userUser

Returns the User currently logged in.

Returns:

  • (User)

    the User currently logged in.



270
271
272
273
# File 'lib/hallon/session.rb', line 270

def user
  user = Spotify.session_user(pointer)
  User.from(user)
end

#usernameString?

Returns username of the currently logged in user.

Returns:

  • (String, nil)

    username of the currently logged in user.



240
241
242
243
# File 'lib/hallon/session.rb', line 240

def username
  username = Spotify.session_user_name(pointer)
  username unless username.nil? or username.empty?
end

#wait_until_logged_inObject (private)

Note:

You must call #login or #relogin before you call this method, or it will hang forever!

Waits until we’re either logged in or a failure occurs.

See Also:



475
476
477
478
479
480
# File 'lib/hallon/session.rb', line 475

def wait_until_logged_in
  wait_for(:logged_in, :connection_error) do |event, error|
    Error.maybe_raise(error)
    session.logged_in?
  end
end