hallon 0.4.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitmodules +3 -0
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG +30 -6
  4. data/README.markdown +7 -7
  5. data/Rakefile +70 -16
  6. data/examples/logging_in.rb +3 -3
  7. data/examples/printing_link_information.rb +1 -1
  8. data/examples/show_published_playlists_of_user.rb +92 -0
  9. data/hallon.gemspec +7 -4
  10. data/lib/hallon.rb +16 -4
  11. data/lib/hallon/album.rb +16 -6
  12. data/lib/hallon/album_browse.rb +78 -0
  13. data/lib/hallon/artist.rb +59 -0
  14. data/lib/hallon/artist_browse.rb +89 -0
  15. data/lib/hallon/base.rb +7 -0
  16. data/lib/hallon/enumerator.rb +64 -0
  17. data/lib/hallon/error.rb +8 -6
  18. data/lib/hallon/ext/spotify.rb +3 -3
  19. data/lib/hallon/image.rb +25 -12
  20. data/lib/hallon/link.rb +4 -4
  21. data/lib/hallon/linkable.rb +4 -2
  22. data/lib/hallon/observable.rb +1 -4
  23. data/lib/hallon/player.rb +130 -0
  24. data/lib/hallon/search.rb +128 -0
  25. data/lib/hallon/session.rb +226 -25
  26. data/lib/hallon/toplist.rb +83 -0
  27. data/lib/hallon/track.rb +62 -7
  28. data/lib/hallon/user.rb +6 -6
  29. data/lib/hallon/version.rb +1 -1
  30. data/spec/hallon/album_browse_spec.rb +20 -0
  31. data/spec/hallon/album_spec.rb +12 -7
  32. data/spec/hallon/artist_browse_spec.rb +29 -0
  33. data/spec/hallon/artist_spec.rb +32 -0
  34. data/spec/hallon/enumerator_spec.rb +106 -0
  35. data/spec/hallon/error_spec.rb +10 -0
  36. data/spec/hallon/hallon_spec.rb +5 -1
  37. data/spec/hallon/image_spec.rb +39 -25
  38. data/spec/hallon/linkable_spec.rb +12 -4
  39. data/spec/hallon/observable_spec.rb +5 -0
  40. data/spec/hallon/player_spec.rb +73 -0
  41. data/spec/hallon/search_spec.rb +80 -0
  42. data/spec/hallon/session_spec.rb +187 -6
  43. data/spec/hallon/toplist_spec.rb +40 -0
  44. data/spec/hallon/track_spec.rb +43 -8
  45. data/spec/mockspotify.rb +47 -0
  46. data/spec/mockspotify/.gitignore +5 -0
  47. data/spec/mockspotify/extconf.rb +5 -0
  48. data/spec/mockspotify/mockspotify_spec.rb +41 -0
  49. data/spec/spec_helper.rb +20 -0
  50. data/spec/support/common_objects.rb +84 -7
  51. metadata +72 -20
  52. data/lib/hallon/ext/object.rb +0 -16
@@ -0,0 +1,3 @@
1
+ [submodule "spec/mockspotify/libmockspotify"]
2
+ path = spec/mockspotify/libmockspotify
3
+ url = git://github.com/Burgestrand/libmockspotify.git
@@ -0,0 +1,2 @@
1
+ rvm:
2
+ - 1.9.2
data/CHANGELOG CHANGED
@@ -1,16 +1,40 @@
1
1
  Hallon’s Changelog
2
2
  ==================
3
3
 
4
- NEXT
4
+ v0.8.0
5
5
  ------------------
6
- - No longer uses autotest as development dependency
6
+ [ Added ]
7
+ - Add example for listing track information in playlists
8
+ - Updated to (lib)Spotify v9
9
+ - Full Track subsystem support
10
+ - Full Album subsystem support
11
+ - Full AlbumBrowse subsystem support
12
+ - Full Artist subsystem support
13
+ - Full ArtistBrowse subsystem support
14
+ - Full Toplist subsystem support
15
+ - Full Search subsystem support
16
+ - Allow setting Session connection type/rules
17
+ - Session offline query methods (offline_time_left et al)
18
+ - Work-in-progress Player
19
+ - Add Session relogin support
20
+ - Add Enumerator
21
+ - Use libmockspotify for testing (spec/mockspotify)
7
22
  - Add Hallon::Base class
8
- - Cleaned up specs to use same mocks everywhere
9
- - Album subsystem support (except for #cover and #artist)
10
23
  - Add optional parameter to have Image#id return raw id
11
- - Make Hallon::URI match image URIs
12
24
  - Allow Image.new to accept an image id
13
- - Add Album#cover
25
+ - Add Hallon::API_BUILD
26
+
27
+ [ Fixed ]
28
+ - Improve speed of Session#wait_for for already-loaded cases
29
+ - Error.maybe_raise no longer errors out on timeout
30
+ - from_link now checks for null pointers
31
+ - No longer uses autotest as development dependency
32
+ - Cleaned up specs to use same mocks everywhere
33
+ - Make Hallon::URI match image URIs
34
+
35
+ [ Broke ]
36
+ - Ignore Ruby v1.8.x compatibility
37
+
14
38
 
15
39
  v0.3.0
16
40
  ------------------
@@ -1,8 +1,6 @@
1
- What is Hallon?
1
+ [What is Hallon?][] [![Build Status][]](http://travis-ci.org/Burgestrand/Hallon) ([but that’s okay](https://github.com/Burgestrand/Hallon/issues/33))
2
2
  ===============
3
3
 
4
- > [Hallon, delicious Ruby bindings for libspotify](http://burgestrand.se/articles/hallon-delicious-ruby-bindings-to-libspotify.html)
5
-
6
4
  We rubyists have this awesome [spotify gem][] allowing us to use [libspotify][] from within Ruby, but it has a significant drawback: the `libspotify` API is very hard to use. Now, we can’t have that, so what do we do? We make Hallon!
7
5
 
8
6
  Hallon is Swedish for “Raspberry”, and has been written to satisfy my needs for API simplicity. It provides you with a wrapper around the spotify gem, making the experience of using `libspotify` from Ruby much more enjoyable.
@@ -11,7 +9,7 @@ Hallon would not have been possible if not for these people:
11
9
 
12
10
  - Per Reimers, cracking synchronization bugs with me in the deep night (4 AM) and correcting me when I didn’t know better
13
11
  - [Spotify](http://www.spotify.com/), providing a service worth attention (and my money!)
14
- - [Linus Oleander](https://github.com/oleander), involving me with the `radiofy.se` project, ultimately spawning the necessity of Hallon
12
+ - [Linus Oleander](https://github.com/oleander), involving me with the [radiofy.se](http://radiofy.se) project, ultimately spawning the necessity of Hallon
15
13
  - [Jesper Särnesjö][], creator of [Greenstripes][], making me think of Hallon as an achievable goal
16
14
 
17
15
  Code samples can be found under `examples/` directory.
@@ -55,7 +53,9 @@ License
55
53
  -------
56
54
  Hallon is licensed under a 2-clause (Simplified) BSD license. More information can be found in the `LICENSE.txt` file.
57
55
 
58
- [spotify gem]: https://rubygems.org/gems/spotify
59
- [libspotify]: http://developer.spotify.com/en/libspotify/overview/
60
- [Greenstripes]: http://github.com/sarnesjo/greenstripes
56
+ [spotify gem]: https://rubygems.org/gems/spotify
57
+ [libspotify]: http://developer.spotify.com/en/libspotify/overview/
58
+ [Greenstripes]: http://github.com/sarnesjo/greenstripes
61
59
  [Jesper Särnesjö]: http://jesper.sarnesjo.org/
60
+ [What is Hallon?]: http://burgestrand.se/articles/hallon-delicious-ruby-bindings-to-libspotify.html
61
+ [Build Status]: https://secure.travis-ci.org/Burgestrand/Hallon.png
data/Rakefile CHANGED
@@ -27,24 +27,58 @@ task 'spotify:coverage' do
27
27
  require 'set'
28
28
  require 'spotify'
29
29
 
30
- dynamicly_used = []
31
- dynamicly_used << "link_create_from_track" # lib/hallon/track.rb
32
- dynamicly_used << "track_add_ref" # lib/ext/spotify.rb
33
- dynamicly_used << "track_release" # lib/ext/spotify.rb
34
- dynamicly_used << "link_create_from_image" # lib/hallon/image.rb
35
- dynamicly_used << "image_release" # lib/ext/spotify.rb
36
- dynamicly_used << "link_create_from_user" # lib/hallon/user.rb
37
- dynamicly_used << "user_add_ref" # lib/ext/spotify.rb
38
- dynamicly_used << "user_release" # lib/ext/spotify.rb
39
- dynamicly_used << "link_as_track" # IGNORE
40
- dynamicly_used << "link_release" # lib/ext/spotify.rb
41
-
42
- methods = Spotify.methods(false).map(&:to_s) - dynamicly_used
30
+ begin
31
+ require 'ruby_parser'
32
+ rescue LoadError
33
+ puts "You need ruby_parser for the spotify:coverage rake task"
34
+ abort
35
+ end
36
+
37
+ methods = Spotify.methods(false).map(&:to_s)
43
38
  covered = Set.new(methods)
44
- matcher = /Spotify(?:::|\.)([\w_]+)[ \(]/
39
+ ignored = [
40
+ 'session_release', # segfaults on libspotify <= 9
41
+ 'session_userdata', # wont support this
42
+ 'link_as_track', # using link_as_track_and_offset instead
43
+ 'toplistbrowse_add_ref', # toplistbrowse creates its’ own pointer
44
+ 'artistbrowse_add_ref', # artistbrowse creates its’ own pointer
45
+ 'albumbrowse_add_ref', # albumbrowse creates its’ own pointer
46
+ 'link_add_ref', # all creation of links has +1 ref
47
+ 'image_add_ref', # all creation of image has +1 ref
48
+ 'search_add_ref', # search creates its’ own pointer
49
+ ]
50
+
51
+ covered -= ignored
52
+
53
+ # Handlers for different AST nodes
54
+ printer = proc { |*args| p args }
55
+ silencer = proc { }
56
+ handlers = Hash.new(Hash.new(silencer))
57
+
58
+ # Direct calls
59
+ handlers[Sexp.new(:const, :Spotify)] = Hash.new(proc { |_, meth, _| meth })
60
+
61
+ # Spotify Pointer
62
+ pointer = handlers[Sexp.new(:colon2, [:const, :Spotify], :Pointer)] = Hash.new(printer)
63
+ pointer[:new] = proc do |recv, meth, (_, ptr, name, release)|
64
+ name = name.value
65
+ release &&= release.value != :false
66
+ ["#{name}_release", "#{name if release}_add_ref"]
67
+ end
68
+
69
+ # DSL Methods
70
+ no_receiver = handlers[nil] = Hash.new(silencer)
71
+ no_receiver[:from_link] = no_receiver[:to_link] = proc do |recv, meth, (_, name)|
72
+ prefix = meth == :to_link ? "link_create" : "link"
73
+ "%s_%s" % [prefix, name.value]
74
+ end
45
75
 
46
76
  FileList['lib/**/*.rb'].each do |file|
47
- File.read(file).scan(matcher) { |method, _| covered.delete(method) }
77
+ ast = RubyParser.new.parse File.read(file)
78
+ ast.each_of_type(:call) do |_, recv, meth, args, *rest|
79
+ name = handlers[recv][meth].call(recv, meth, args)
80
+ covered.subtract Array(name).map(&:to_s)
81
+ end
48
82
  end
49
83
 
50
84
  covered.group_by { |m| m[/[^_]+/] }.each_pair do |group, methods|
@@ -55,9 +89,24 @@ task 'spotify:coverage' do
55
89
  puts
56
90
  end
57
91
 
92
+ puts "Ignored:"
93
+ ignored.each_slice(3) do |slice|
94
+ puts "\t#{slice.join(', ')}"
95
+ end
96
+ puts
97
+
58
98
  puts "Coverage: %.02f%%" % (100 * (1 - covered.size.fdiv(methods.size)))
59
99
  end
60
100
 
101
+ desc "Compile mockspotify"
102
+ task 'mock:compile' do
103
+ Dir.chdir 'spec/mockspotify' do
104
+ sh 'ruby extconf.rb'
105
+ sh 'make'
106
+ end
107
+ end
108
+
109
+ task :spec => 'mock:compile'
61
110
  task :test => :spec
62
111
 
63
112
  #
@@ -70,7 +119,12 @@ end
70
119
 
71
120
  desc "Remove generated files"
72
121
  task :clean do
73
- sh 'git clean -fdx --exclude Gemfile.lock --exclude spec/support/config.rb'
122
+ print "Do you really want to delete all non-git tracked files? (y/n) [n]: "
123
+ if STDIN.gets.chomp == 'y'
124
+ sh 'git clean -fdx --exclude Gemfile.lock --exclude spec/support/config.rb'
125
+ else
126
+ puts "Whew. Close one!"
127
+ end
74
128
  end
75
129
 
76
130
  task :default => [:spec]
@@ -2,7 +2,7 @@
2
2
  require 'hallon'
3
3
  require './spec/support/config'
4
4
 
5
- session = Hallon::Session.instance IO.read(ENV['HALLON_APPKEY']) do
5
+ session = Hallon::Session.initialize IO.read(ENV['HALLON_APPKEY']) do
6
6
  on(:log_message) do |message|
7
7
  puts "[LOG] #{message}"
8
8
  end
@@ -10,8 +10,8 @@ end
10
10
 
11
11
  session.login ENV['HALLON_USERNAME'], ENV['HALLON_PASSWORD']
12
12
 
13
- session.process_events_on(:logged_in) { |error| Hallon::Error.maybe_raise(error) }
14
- session.process_events_on(:connection_error) do |error|
13
+ session.wait_for(:logged_in) { |error| Hallon::Error.maybe_raise(error) }
14
+ session.wait_for(:connection_error) do |error|
15
15
  session.logged_in? or Hallon::Error.maybe_raise(error)
16
16
  end
17
17
 
@@ -9,7 +9,7 @@ def prompt(str)
9
9
  end
10
10
 
11
11
  # Hallon
12
- session = Hallon::Session.instance IO.read(ENV['HALLON_APPKEY']) do
12
+ session = Hallon::Session.initialize IO.read(ENV['HALLON_APPKEY']) do
13
13
  on(:log_message) do |message|
14
14
  $stderr.puts "[LOG] #{message}"
15
15
  end
@@ -0,0 +1,92 @@
1
+ # coding: utf-8
2
+ #
3
+ # DISCLAIMER:
4
+ # This file was written without extensive testing, and is merely a proof
5
+ # of concept. Before using this yourself, I advice you to look through
6
+ # the code carefully.
7
+ #
8
+ # The below code uses the raw Spotify FFI API, and does not represent how
9
+ # this will be done when Hallon has API support for below operations!
10
+ #
11
+ # Hallon API in this file is only used for:
12
+ # - logging in
13
+ # - querying track information
14
+ #
15
+ # Raw Spotify FFI API is used for:
16
+ # - fetching playlist container
17
+ # - fetching playlists
18
+ # - fetching tracks from playlists
19
+ require 'hallon'
20
+ require './spec/support/config'
21
+
22
+ # Utility
23
+ def prompt(str)
24
+ print str
25
+ gets.chomp
26
+ end
27
+
28
+ session = Hallon::Session.initialize IO.read(ENV['HALLON_APPKEY']) do
29
+ on(:log_message) do |message|
30
+ puts "[LOG] #{message}"
31
+ end
32
+
33
+ on(:connection_error) do |error|
34
+ Hallon::Error.maybe_raise(error)
35
+ end
36
+
37
+ on(:logged_out) do
38
+ abort "[FAIL] Logged out!"
39
+ end
40
+ end
41
+
42
+ session.login ENV['HALLON_USERNAME'], ENV['HALLON_PASSWORD']
43
+
44
+ session.wait_for(:logged_in) { |error| Hallon::Error.maybe_raise(error) }
45
+ session.wait_for(:connection_error) do |error|
46
+ session.logged_in? or Hallon::Error.maybe_raise(error)
47
+ end
48
+
49
+ puts "Successfully logged in!"
50
+
51
+ # Hallon does not have support for the below operations, so we resort
52
+ # to using the raw Spotify gem and FFI for now.
53
+ while username = prompt("Enter a Spotify username: ")
54
+ begin
55
+ username = nil if username.empty?
56
+
57
+ puts "Fetching container for #{username || "current user"}..."
58
+ container = Spotify::session_publishedcontainer_for_user_create(session.pointer, username)
59
+ if container.null?
60
+ puts "Failed (unknown reason)."
61
+ next
62
+ end
63
+
64
+ session.wait_for { Spotify::playlistcontainer_is_loaded(container) }
65
+
66
+ num_playlists = Spotify::playlistcontainer_num_playlists(container)
67
+ puts "Listing #{num_playlists} playlists."
68
+
69
+ num_playlists.times do |i|
70
+ playlist = Spotify::playlistcontainer_playlist(container, i)
71
+ session.wait_for { Spotify::playlist_is_loaded(playlist) }
72
+
73
+ puts
74
+ puts Spotify::playlist_name(playlist) << ": "
75
+
76
+ num_tracks = Spotify::playlist_num_tracks(playlist)
77
+ num_tracks.times do |j|
78
+ # Here we go back into Hallon API, passing the raw pointer
79
+ # to Hallon::Track.new; this means all of Hallon::Track API
80
+ # is supported on “track” here!
81
+ track = Hallon::Track.new(Spotify::playlist_track(playlist, j))
82
+ session.wait_for { track.loaded? }
83
+
84
+ puts "\t (#{j+1}/#{num_tracks}) #{track.name}"
85
+ end
86
+ end
87
+ rescue Interrupt
88
+ # do nothing, continue with loop
89
+ ensure
90
+ Spotify::playlistcontainer_release(container) unless container.nil?
91
+ end
92
+ end
@@ -1,5 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require './lib/hallon/version'
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require 'hallon/version'
3
5
 
4
6
  Gem::Specification.new do |gem|
5
7
  gem.name = "hallon"
@@ -7,9 +9,10 @@ Gem::Specification.new do |gem|
7
9
  gem.homepage = "http://github.com/Burgestrand/Hallon"
8
10
  gem.authors = ["Kim Burgestrand"]
9
11
  gem.email = 'kim@burgestrand.se'
10
- gem.license = 'GNU AGPL'
12
+ gem.license = 'X11 License'
11
13
 
12
14
  gem.files = `git ls-files`.split("\n")
15
+ gem.files += `cd spec/mockspotify/libmockspotify && git ls-files src`.split("\n").map { |path| "spec/mockspotify/libmockspotify/#{path}" }
13
16
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
17
  gem.executables = []
15
18
  gem.require_paths = ["lib"]
@@ -18,8 +21,8 @@ Gem::Specification.new do |gem|
18
21
  gem.platform = Gem::Platform::RUBY
19
22
  gem.required_ruby_version = '~> 1.8'
20
23
 
21
- gem.add_dependency 'spotify', '~> 8.0.5'
22
- gem.add_development_dependency 'mockspotify', '~> 0.1.8'
24
+ gem.add_dependency 'spotify', '~> 9.0.0'
25
+ gem.add_development_dependency 'bundler', '~> 1.0'
23
26
  gem.add_development_dependency 'rake', '~> 0.8'
24
27
  gem.add_development_dependency 'rspec', '~> 2'
25
28
  gem.add_development_dependency 'yard'
@@ -2,7 +2,6 @@
2
2
  require 'spotify'
3
3
  require 'hallon/ext/spotify'
4
4
  require 'hallon/ext/ffi'
5
- require 'hallon/ext/object'
6
5
 
7
6
  require 'hallon/synchronizable'
8
7
  require 'hallon/observable'
@@ -11,12 +10,20 @@ require 'hallon/linkable'
11
10
  require 'hallon/version'
12
11
  require 'hallon/error'
13
12
  require 'hallon/base'
13
+ require 'hallon/enumerator'
14
14
  require 'hallon/session'
15
15
  require 'hallon/link'
16
16
  require 'hallon/user'
17
17
  require 'hallon/image'
18
18
  require 'hallon/track'
19
19
  require 'hallon/album'
20
+ require 'hallon/artist'
21
+ require 'hallon/toplist'
22
+
23
+ require 'hallon/album_browse'
24
+ require 'hallon/artist_browse'
25
+ require 'hallon/player'
26
+ require 'hallon/search'
20
27
 
21
28
  # The Hallon module wraps around all Hallon objects to avoid polluting
22
29
  # the global namespace. To start using Hallon, you most likely want to
@@ -25,15 +32,20 @@ module Hallon
25
32
  # @see Spotify::API_VERSION
26
33
  API_VERSION = Spotify::API_VERSION
27
34
 
35
+ # Spotify API build.
36
+ #
37
+ # @see Spotify#api_build
38
+ API_BUILD = Spotify.build_id
39
+
28
40
  # A regex that matches all Spotify URIs
29
41
  #
30
42
  # @example
31
43
  # Hallon::URI === "spotify:user:burgestrand" # => true
32
44
  URI = /(spotify:(?:
33
- (?:artist|album|track|user:[^:]+:playlist):\h+
45
+ (?:artist|album|track|user:[^:]+:playlist):[a-fA-F0-9]+
34
46
  |user:[^:]+
35
- |search:(?:[-\w$\.+!*'(),]+|%\h{2})+
36
- |image:\h{40}
47
+ |search:(?:[-\w$\.+!*'(),]+|%[a-fA-F0-9]{2})+
48
+ |image:[a-fA-F0-9]{40}
37
49
  ))
38
50
  /x
39
51
  end
@@ -9,8 +9,9 @@ module Hallon
9
9
  #
10
10
  # @see http://developer.spotify.com/en/libspotify/docs/group__album.html
11
11
  class Album < Base
12
+ # An array of different kinds of albums. Singles, compilations etc.
12
13
  def self.types
13
- Spotify.enum_type(:albumtype).to_hash
14
+ Spotify.enum_type(:albumtype).symbols
14
15
  end
15
16
 
16
17
  extend Linkable
@@ -63,15 +64,24 @@ module Hallon
63
64
  # Retrieve album cover art.
64
65
  #
65
66
  # @return [Image]
66
- def cover(session = Session.instance)
67
+ def cover
67
68
  image_id = Spotify.album_cover(@pointer)
68
- Hallon::Image.new(image_id.read_string(20), session) unless image_id.null?
69
+ Image.new(image_id.read_string(20)) unless image_id.null?
69
70
  end
70
71
 
71
- # Not yet implemented.
72
+ # Retrieve the album Artist.
73
+ #
74
+ # @return [Artist, nil]
72
75
  def artist
73
- return if (ptr = Spotify.album_artist(@pointer)).null?
74
- raise NotImplementedError
76
+ artist = Spotify.album_artist(@pointer)
77
+ Artist.new(artist) unless artist.null?
78
+ end
79
+
80
+ # Retrieve an AlbumBrowse object for this Album.
81
+ #
82
+ # @return [AlbumBrowse]
83
+ def browse
84
+ AlbumBrowse.new(pointer)
75
85
  end
76
86
  end
77
87
  end
@@ -0,0 +1,78 @@
1
+ module Hallon
2
+ # AlbumBrowse objects are for retrieving additional data from
3
+ # an album that cannot otherwise be acquired. This includes
4
+ # tracks, reviews, copyright information.
5
+ #
6
+ # AlbumBrowse object triggers the `:load` callback on itself
7
+ # when it loads.
8
+ #
9
+ # @example
10
+ # browse = album.browse # album is a Hallon::Album
11
+ # browse.on(:load) do
12
+ # puts "Album browser for #{browse.album.name} has been loaded!"
13
+ # end
14
+ # session.wait_for { browse.loaded? } # will eventually trigger above callback
15
+ #
16
+ # @see Album
17
+ # @see http://developer.spotify.com/en/libspotify/docs/group__albumbrowse.html
18
+ class AlbumBrowse < Base
19
+ include Observable
20
+
21
+ # Creates an AlbumBrowse instance from an Album or an Album pointer.
22
+ #
23
+ # @note Use {Album#browse} to browse an Album.
24
+ # @param [Album, FFI::Pointer] album
25
+ def initialize(album)
26
+ album = album.pointer if album.respond_to?(:pointer)
27
+ @callback = proc { trigger(:load) }
28
+
29
+ albumbrowse = Spotify.albumbrowse_create(session.pointer, album, @callback, nil)
30
+ @pointer = Spotify::Pointer.new(albumbrowse, :albumbrowse, false)
31
+ end
32
+
33
+ # @return [Boolean] true if the album is loaded
34
+ def loaded?
35
+ Spotify.albumbrowse_is_loaded(@pointer)
36
+ end
37
+
38
+ # @see Error
39
+ # @return [Symbol] album browser error status
40
+ def error
41
+ Spotify.albumbrowse_error(@pointer)
42
+ end
43
+
44
+ # @return [String] album review
45
+ def review
46
+ Spotify.albumbrowse_review(@pointer)
47
+ end
48
+
49
+ # @return [Artist] artist performing this album
50
+ def artist
51
+ pointer = Spotify.albumbrowse_artist(@pointer)
52
+ Artist.new(pointer) unless pointer.null?
53
+ end
54
+
55
+ # @return [Album] album this object is browsing
56
+ def album
57
+ pointer = Spotify.albumbrowse_album(@pointer)
58
+ Album.new(pointer) unless pointer.null?
59
+ end
60
+
61
+ # @return [Enumerator<String>] list of copyright notices
62
+ def copyrights
63
+ size = Spotify.albumbrowse_num_copyrights(@pointer)
64
+ Enumerator.new(size) do |i|
65
+ Spotify.albumbrowse_copyright(@pointer, i)
66
+ end
67
+ end
68
+
69
+ # @return [Enumerator<Track>] list of tracks
70
+ def tracks
71
+ size = Spotify.albumbrowse_num_tracks(@pointer)
72
+ Enumerator.new(size) do |i|
73
+ pointer = Spotify.albumbrowse_track(@pointer, i)
74
+ Track.new(pointer) unless pointer.null?
75
+ end
76
+ end
77
+ end
78
+ end