spotify 12.5.3 → 12.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +20 -5
  4. data/CHANGELOG.md +29 -1
  5. data/Gemfile +5 -0
  6. data/MIT-LICENSE +21 -0
  7. data/README.markdown +75 -50
  8. data/Rakefile +1 -1
  9. data/examples/example-audio_delivery_speed.rb +48 -0
  10. data/examples/{audio-stream_example.rb → example-audio_stream.rb} +14 -29
  11. data/examples/example-console.rb +9 -0
  12. data/examples/example-listing_playlists.rb +89 -0
  13. data/examples/example-loading_object.rb +25 -0
  14. data/examples/example-random_related_artists.rb +53 -0
  15. data/examples/support.rb +106 -0
  16. data/lib/spotify.rb +36 -55
  17. data/lib/spotify/api.rb +54 -26
  18. data/lib/spotify/api/album.rb +45 -2
  19. data/lib/spotify/api/album_browse.rb +81 -3
  20. data/lib/spotify/api/artist.rb +21 -2
  21. data/lib/spotify/api/artist_browse.rb +121 -3
  22. data/lib/spotify/api/error.rb +5 -1
  23. data/lib/spotify/api/image.rb +72 -6
  24. data/lib/spotify/api/inbox.rb +33 -4
  25. data/lib/spotify/api/link.rb +117 -4
  26. data/lib/spotify/api/miscellaneous.rb +12 -0
  27. data/lib/spotify/api/playlist.rb +321 -16
  28. data/lib/spotify/api/playlist_container.rb +168 -9
  29. data/lib/spotify/api/search.rb +156 -3
  30. data/lib/spotify/api/session.rb +390 -26
  31. data/lib/spotify/api/toplist_browse.rb +74 -3
  32. data/lib/spotify/api/track.rb +134 -4
  33. data/lib/spotify/api/user.rb +18 -2
  34. data/lib/spotify/api_helpers.rb +47 -0
  35. data/lib/spotify/data_converters.rb +7 -0
  36. data/lib/spotify/{types → data_converters}/best_effort_string.rb +1 -1
  37. data/lib/spotify/{types → data_converters}/byte_string.rb +0 -0
  38. data/lib/spotify/data_converters/country_code.rb +30 -0
  39. data/lib/spotify/{types → data_converters}/image_id.rb +1 -1
  40. data/lib/spotify/{type_safety.rb → data_converters/type_safety.rb} +0 -0
  41. data/lib/spotify/{types → data_converters}/utf8_string.rb +2 -2
  42. data/lib/spotify/{types → data_converters}/utf8_string_pointer.rb +2 -2
  43. data/lib/spotify/error.rb +180 -47
  44. data/lib/spotify/managed_pointer.rb +32 -12
  45. data/lib/spotify/monkey_patches/ffi_buffer.rb +11 -0
  46. data/lib/spotify/monkey_patches/ffi_enums.rb +4 -0
  47. data/lib/spotify/monkey_patches/ffi_pointer.rb +1 -0
  48. data/lib/spotify/structs.rb +4 -0
  49. data/lib/spotify/structs/session_callbacks.rb +97 -26
  50. data/lib/spotify/structs/session_config.rb +1 -1
  51. data/lib/spotify/structs/subscribers.rb +4 -3
  52. data/lib/spotify/types.rb +104 -5
  53. data/lib/spotify/{objects → types}/album.rb +0 -0
  54. data/lib/spotify/{objects → types}/album_browse.rb +0 -0
  55. data/lib/spotify/{objects → types}/artist.rb +0 -0
  56. data/lib/spotify/{objects → types}/artist_browse.rb +0 -0
  57. data/lib/spotify/{objects → types}/image.rb +0 -0
  58. data/lib/spotify/{objects → types}/inbox.rb +0 -0
  59. data/lib/spotify/{objects → types}/link.rb +0 -0
  60. data/lib/spotify/{objects → types}/playlist.rb +0 -0
  61. data/lib/spotify/{objects → types}/playlist_container.rb +0 -0
  62. data/lib/spotify/{objects → types}/search.rb +0 -0
  63. data/lib/spotify/{objects → types}/session.rb +0 -0
  64. data/lib/spotify/{objects → types}/toplist_browse.rb +0 -0
  65. data/lib/spotify/{objects → types}/track.rb +0 -0
  66. data/lib/spotify/{objects → types}/user.rb +0 -0
  67. data/lib/spotify/util.rb +38 -35
  68. data/lib/spotify/version.rb +1 -1
  69. data/spec/spec_helper.rb +24 -13
  70. data/spec/spotify/api/image_spec.rb +32 -0
  71. data/spec/spotify/api/inbox_spec.rb +34 -0
  72. data/spec/spotify/api/link_spec.rb +40 -0
  73. data/spec/spotify/api/playlist_spec.rb +99 -0
  74. data/spec/spotify/api/playlistcontainer_spec.rb +82 -0
  75. data/spec/spotify/api/session_spec.rb +97 -0
  76. data/spec/spotify/api/track_spec.rb +29 -0
  77. data/spec/spotify/api_error_spec.rb +55 -0
  78. data/spec/spotify/api_spec.rb +17 -11
  79. data/spec/spotify/{types → data_converters}/best_effort_string_spec.rb +4 -4
  80. data/spec/spotify/{types → data_converters}/byte_string_spec.rb +0 -0
  81. data/spec/spotify/data_converters/country_code_spec.rb +16 -0
  82. data/spec/spotify/{types → data_converters}/image_id_spec.rb +1 -1
  83. data/spec/spotify/{type_safety_spec.rb → data_converters/type_safety_spec.rb} +0 -0
  84. data/spec/spotify/{types → data_converters}/utf8_string_pointer_spec.rb +0 -0
  85. data/spec/spotify/{types → data_converters}/utf8_string_spec.rb +1 -1
  86. data/spec/spotify/{api/functions_spec.rb → functions_spec.rb} +2 -0
  87. data/spec/spotify/managed_pointer_spec.rb +13 -13
  88. data/spec/spotify/structs/subscribers_spec.rb +5 -3
  89. data/spec/spotify/{defines_spec.rb → types_spec.rb} +16 -3
  90. data/spec/spotify/util_spec.rb +24 -0
  91. data/spec/spotify_spec.rb +74 -0
  92. data/spec/support/spotify_util.rb +6 -2
  93. data/spec/support/spy_output.rb +16 -0
  94. data/spotify.gemspec +4 -2
  95. metadata +111 -71
  96. data/examples/README.md +0 -15
  97. data/examples/console_example.rb +0 -22
  98. data/examples/example_support.rb +0 -66
  99. data/examples/loading-object_example.rb +0 -43
  100. data/examples/logging-in_example.rb +0 -58
  101. data/lib/spotify/defines.rb +0 -109
  102. data/lib/spotify/objects.rb +0 -17
  103. data/spec/spotify/enums_spec.rb +0 -9
  104. data/spec/spotify/spotify_spec.rb +0 -69
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative "support"
5
+
6
+ session = Support.initialize_spotify!
7
+
8
+ track_uri = Support.prompt("Please enter a track URI", "spotify:track:1612JQ4JxS8bm5ky53N3bH")
9
+ link = Spotify.link_create_from_string(track_uri)
10
+
11
+ if link.nil?
12
+ $logger.error "Invalid URI. Aborting."
13
+ abort
14
+ elsif (link_type = Spotify.link_type(link)) != :track
15
+ $logger.error "Was #{link_type} URI. Needs track. Aborting."
16
+ abort
17
+ else
18
+ track = Spotify.link_as_track(link)
19
+ end
20
+
21
+ $logger.info "Attempting to load track. Waiting forever until successful."
22
+ Support.poll(session) { Spotify.track_is_loaded(track) }
23
+ $logger.info "Track loaded."
24
+
25
+ puts Spotify.track_name(track)
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require_relative "support"
5
+
6
+ session = Support.initialize_spotify!
7
+
8
+ artist_uri = Support.prompt("Please enter an artist URI to start with", "spotify:artist:7sVQKNtdP2NylxMgbNOJMM")
9
+ link = Spotify.link_create_from_string(artist_uri)
10
+
11
+ if link.null?
12
+ $logger.error "Invalid URI. Aborting."
13
+ abort
14
+ elsif (link_type = Spotify.link_type(link)) != :artist
15
+ $logger.error "Was #{link_type} URI. Needs artist. Aborting."
16
+ abort
17
+ end
18
+
19
+ target = Spotify.link_as_artist(link)
20
+
21
+ # Spotify.enum & :no_tracks & :no_albums
22
+ no_tracks = Spotify::Util.enum_value!(:no_tracks, "album type")
23
+ no_albums = Spotify::Util.enum_value!(:no_albums, "album type")
24
+ $dummy_callback = proc { }
25
+
26
+ artists = Set.new
27
+ count = 0
28
+
29
+ while target
30
+ $logger.info "Stats: #{artists.length} artists, #{count} browses."
31
+
32
+ browser = Spotify.artistbrowse_create(session, target, no_tracks | no_albums, $dummy_callback, nil)
33
+
34
+ Support.poll(session) do
35
+ begin
36
+ Spotify.try(:artistbrowse_error, browser)
37
+ rescue Spotify::IsLoadingError
38
+ # Ignore it
39
+ end
40
+
41
+ Spotify.artistbrowse_is_loaded(browser)
42
+ end
43
+
44
+ similar_artists = Spotify.artistbrowse_num_similar_artists(browser).times.map do |index|
45
+ Spotify.artistbrowse_similar_artist(browser, index)
46
+ end
47
+
48
+ similar_artists_names = similar_artists.map { |artist| Spotify.artist_name(artist) }
49
+
50
+ target = similar_artists.sample
51
+ artists << Spotify.artist_name(target)
52
+ count += 1
53
+ end
@@ -0,0 +1,106 @@
1
+ require "bundler/setup"
2
+ require "spotify"
3
+ require "logger"
4
+ require "pry"
5
+ require "io/console"
6
+
7
+ # Kill main thread if any other thread dies.
8
+ Thread.abort_on_exception = true
9
+
10
+ # We use a logger to print some information on when things are happening.
11
+ $stderr.sync = true
12
+ $logger = Logger.new($stderr)
13
+ $logger.level = Logger::INFO
14
+ $logger.formatter = proc do |severity, datetime, progname, msg|
15
+ progname = if progname
16
+ " (#{progname}) "
17
+ else
18
+ " "
19
+ end
20
+ "\n[#{severity} @ #{datetime.strftime("%H:%M:%S")}]#{progname}#{msg}"
21
+ end
22
+
23
+ #
24
+ # Some utility.
25
+ #
26
+
27
+ module Support
28
+ module_function
29
+
30
+ DEFAULT_CONFIG = {
31
+ api_version: Spotify::API_VERSION.to_i,
32
+ application_key: File.binread("./spotify_appkey.key"),
33
+ cache_location: ".spotify/",
34
+ settings_location: ".spotify/",
35
+ user_agent: "spotify for ruby",
36
+ callbacks: Spotify::SessionCallbacks.new
37
+ }
38
+
39
+ def logger
40
+ $logger
41
+ end
42
+
43
+ class << self
44
+ attr_accessor :silenced
45
+ end
46
+
47
+ # libspotify supports callbacks, but they are not useful for waiting on
48
+ # operations (how they fire can be strange at times, and sometimes they
49
+ # might not fire at all). As a result, polling is the way to go.
50
+ def poll(session, idle_time = 0.05)
51
+ until yield
52
+ print "." unless silenced
53
+ process_events(session)
54
+ sleep(idle_time)
55
+ end
56
+ end
57
+
58
+ # Process libspotify events once.
59
+ def process_events(session)
60
+ Spotify.session_process_events(session)
61
+ end
62
+
63
+ # Ask the user for input with a prompt explaining what kind of input.
64
+ def prompt(message, default = nil)
65
+ if default
66
+ print "\n#{message} [#{default}]: "
67
+ input = gets.chomp
68
+
69
+ if input.empty?
70
+ default
71
+ else
72
+ input
73
+ end
74
+ else
75
+ print "\n#{message}: "
76
+ gets.chomp
77
+ end
78
+ end
79
+
80
+ def initialize_spotify!(config = DEFAULT_CONFIG)
81
+ error, session = Spotify.session_create(config)
82
+ raise error if error.is_a?(Spotify::APIError)
83
+
84
+ if username = Spotify.session_remembered_user(session)
85
+ logger.info "Using remembered login for: #{username}."
86
+ Spotify.try(:session_relogin, session)
87
+ else
88
+ username = prompt("Spotify username, or Facebook e-mail")
89
+ password = $stdin.noecho { prompt("Spotify password, or Facebook password") }
90
+
91
+ logger.info "Attempting login with #{username}."
92
+ Spotify.try(:session_login, session, username, password, true, nil)
93
+ end
94
+
95
+ logger.info "Log in requested. Waiting forever until logged in."
96
+ Support.poll(session) { Spotify.session_connectionstate(session) == :logged_in }
97
+
98
+ at_exit do
99
+ logger.info "Logging out."
100
+ Spotify.session_logout(session)
101
+ Support.poll(session) { Spotify.session_connectionstate(session) != :logged_in }
102
+ end
103
+
104
+ session
105
+ end
106
+ end
@@ -1,8 +1,15 @@
1
1
  # encoding: utf-8
2
2
  require 'ffi'
3
3
  require 'spotify/monkey_patches/ffi_pointer'
4
+ require 'spotify/monkey_patches/ffi_buffer'
5
+ require 'spotify/monkey_patches/ffi_enums'
6
+
4
7
  require 'libspotify'
5
- require 'monitor'
8
+ require 'performer'
9
+
10
+ require 'spotify/version'
11
+ require 'spotify/util'
12
+ require 'spotify/api'
6
13
 
7
14
  # Spotify module allows you to place calls against the Spotify::API.
8
15
  #
@@ -10,61 +17,38 @@ require 'monitor'
10
17
  # @see Spotify::API Spotify::API on available libspotify methods
11
18
  # @see http://developer.spotify.com/en/libspotify/docs/ official libspotify documentation
12
19
  module Spotify
13
- # API is the class which has all libspotify functions attached.
14
- #
15
- # All functions are attached as both instance methods and class methods, mainly
16
- # because that’s how FFI works it’s magic with attach_function. However, as this
17
- # is a class it allows to be instantiated.
18
- #
19
- # @note The API is private because this class is an implementation detail.
20
- #
21
- # @note You should never call any Spotify::API.method() directly, but instead
22
- # you should call them via Spotify.method(). libspotify is not thread-safe,
23
- # but it is documented to be okay to call the API from multiple threads *if*
24
- # you only call one function at a time, which is ensured by the lock in the
25
- # Spotify module.
26
- #
27
- # @api private
28
- class API
29
- extend FFI::Library
30
-
31
- begin
32
- ffi_lib [LIBSPOTIFY_BIN, 'spotify', 'libspotify', '/Library/Frameworks/libspotify.framework/libspotify']
33
- ffi_convention :stdcall if FFI::Platform.windows?
34
- rescue LoadError
35
- puts <<-ERROR.gsub(/^ */, '')
36
- Failed to load the `libspotify` library. It is possible that the libspotify gem
37
- does not exist for your platform, in which case you’ll need to install it manually.
20
+ # @return [String] libspotify build ID.
21
+ API_BUILD = Spotify::API.build_id
38
22
 
39
- For manual installation instructions, please see:
40
- https://github.com/Burgestrand/Hallon/wiki/How-to-install-libspotify
41
- ERROR
42
- raise
43
- end
23
+ unless API_BUILD.include?(Spotify::API_VERSION)
24
+ warn "[WARNING:#{__FILE__}] libspotify v#{build_id} might be incompatible with ruby spotify v#{VERSION}(#{API_VERSION})"
44
25
  end
45
26
 
27
+ @performer = Performer.new
46
28
  @__api__ = Spotify::API
47
- @__api__.extend(MonitorMixin)
48
29
 
49
30
  class << self
31
+ # @see https://rubygems.org/gems/performer
32
+ # @return [Performer]
33
+ attr_reader :performer
34
+
50
35
  # Like send, but raises an error if the method returns a non-OK error.
51
36
  #
52
37
  # @example calling a method that returns an error
53
38
  # Spotify.relogin(session) # => :invalid_indata
54
- # Spotify.try(:relogin, session) # => raises Spotify::Error
39
+ # Spotify.try(:relogin, session) # => raises APIError
55
40
  #
56
41
  # @note Works for non-error returning methods as well, it just does
57
42
  # not do anything interesting.
58
43
  #
59
44
  # @param [#to_s] name
60
45
  # @param args
61
- # @raise [Spotify::Error] if an error other than :ok is returned
46
+ # @raise [APIError] if an error other than :ok is returned
62
47
  def try(name, *args, &block)
63
48
  public_send(name, *args, &block).tap do |error|
64
- error, symbol = Spotify::Error.disambiguate(error)
65
- next if symbol.nil?
66
- next if symbol == :ok
67
- raise Error.new(symbol)
49
+ if error.is_a?(APIError)
50
+ raise error unless error.is_a?(IsLoadingError)
51
+ end
68
52
  end
69
53
  end
70
54
 
@@ -80,9 +64,7 @@ module Spotify
80
64
  # @param [Boolean] include_private
81
65
  # @return [Boolean] true if the API supports the given method.
82
66
  def respond_to_missing?(name, include_private = false)
83
- @__api__.synchronize do
84
- @__api__.respond_to?(name, include_private)
85
- end
67
+ @__api__.respond_to?(name, include_private)
86
68
  end
87
69
 
88
70
  # Calls the any method on the underlying {Spotify::API}.
@@ -90,25 +72,24 @@ module Spotify
90
72
  # @example calling the API
91
73
  # Spotify.link_create_from_string("spotify:user:burgestrand") # => #<Spotify::Link address=0x0deadbeef>
92
74
  #
93
- # @note Spotify protects all calls to {Spotify::API} with a lock, so it is
94
- # considered safe to call the API from different threads. The lock
95
- # is re-entrant.
75
+ # @note Spotify protects all calls to {Spotify::API} by calling all
76
+ # API methods in the {.performer} thread.
96
77
  #
97
78
  # @param [Symbol, String] name
98
79
  # @param [Object, …] args
99
80
  def method_missing(name, *args, &block)
100
- @__api__.synchronize do
101
- @__api__.send(name, *args, &block)
81
+ if respond_to?(name)
82
+ performer.sync { @__api__.public_send(name, *args, &block) }
83
+ else
84
+ super
102
85
  end
103
86
  end
87
+
88
+ # Print debug messages, if $DEBUG is true.
89
+ #
90
+ # @param [String] message
91
+ def log(message)
92
+ $stdout.puts "[#{caller[0]}] #{message}" if $DEBUG
93
+ end
104
94
  end
105
95
  end
106
-
107
- require 'spotify/version'
108
- require 'spotify/util'
109
- require 'spotify/types'
110
- require 'spotify/error'
111
- require 'spotify/objects'
112
- require 'spotify/defines'
113
- require 'spotify/structs'
114
- require 'spotify/api'
@@ -1,49 +1,77 @@
1
1
  module Spotify
2
+ # API is the class which has all libspotify functions attached.
3
+ #
4
+ # All functions are attached as both instance methods and class methods, mainly
5
+ # because that’s how FFI works it’s magic with attach_function. However, as this
6
+ # is a class it allows to be instantiated.
7
+ #
8
+ # @note The API is private because this class is an implementation detail.
9
+ #
10
+ # @note You should never call any Spotify::API.method() directly, but instead
11
+ # you should call them via Spotify.method(). libspotify is not thread-safe,
12
+ # but it is documented to be okay to call the API from multiple threads *if*
13
+ # you only call one function at a time, which is ensured by the lock in the
14
+ # Spotify module.
15
+ #
16
+ # @api private
2
17
  class API
3
- # @!macro [attach] attach_function
4
- # @!method $1(…)
5
- # @!scope class
6
- # @!scope instance
7
- # @example method signature (shows arguments)
8
- # $*
9
- # @return [${-1}]
10
- #
18
+ extend FFI::Library
19
+
20
+ begin
21
+ ffi_lib [LIBSPOTIFY_BIN, 'spotify', 'libspotify', '/Library/Frameworks/libspotify.framework/libspotify']
22
+ ffi_convention :stdcall if FFI::Platform.windows?
23
+ rescue LoadError
24
+ $stderr.puts <<-ERROR.gsub(/^ */, '')
25
+ Failed to load the `libspotify` library. It is possible that the libspotify gem
26
+ does not exist for your platform, in which case you'll need to install it manually.
27
+
28
+ For manual installation instructions, please see:
29
+ https://github.com/Burgestrand/Hallon/wiki/How-to-install-libspotify
30
+ ERROR
31
+ raise
32
+ end
33
+
11
34
  # Overloaded to ensure all methods are defined as blocking,
12
35
  # and they return a managed pointer with the correct refcount.
13
36
  #
14
37
  # @param [#to_s] name function name sans `sp_` prefix.
15
38
  # @param [Array] args
16
39
  # @param [Object] returns
17
- def self.attach_function(c_name = nil, name, args, returns)
40
+ def self.attach_function(c_name = nil, name, args, returns, &block)
18
41
  if returns.respond_to?(:retaining_class) && name !~ /create/
19
42
  returns = returns.retaining_class
20
43
  end
21
44
 
22
- options = { :blocking => true }
23
- c_name ||= "sp_#{name}"
45
+ options = { blocking: true }
46
+ name = name.to_sym
47
+ c_name ||= :"sp_#{name}"
24
48
  super(name, c_name, args, returns, options)
25
- end
26
49
 
27
- # Now, make sure we have the right libspotify version.
50
+ if block_given?
51
+ alias_method c_name, name
52
+ define_method name, &block
28
53
 
29
- # @!group Miscellaneous
54
+ singleton_class.instance_eval do
55
+ alias_method c_name, name
56
+ define_method name, &block
57
+ end
30
58
 
31
- # @see Spotify::API_BUILD
32
- attach_function :build_id, [], UTF8String
33
-
34
- # @!endgroup
35
- end
36
-
37
- # @return [String] libspotify build ID.
38
- API_BUILD = Spotify.build_id
59
+ name
60
+ end
61
+ end
39
62
 
40
- # No support yet for "similar" versions, so it’s a hard requirement
41
- # on the libspotify version. It *must* be the same, even patch version.
42
- unless API_BUILD.include?(Spotify::API_VERSION)
43
- raise LoadError, "libspotify v#{build_id} is incompatible with ruby spotify v#{VERSION}(#{API_VERSION})"
63
+ require "spotify/api_helpers"
64
+ extend APIHelpers
65
+ include APIHelpers
44
66
  end
45
67
  end
46
68
 
69
+ require 'spotify/error'
70
+ require 'spotify/data_converters'
71
+ require 'spotify/types'
72
+ require 'spotify/structs'
73
+
74
+ require 'spotify/api/miscellaneous'
47
75
  require 'spotify/api/album'
48
76
  require 'spotify/api/album_browse'
49
77
  require 'spotify/api/artist'
@@ -1,14 +1,57 @@
1
1
  module Spotify
2
2
  class API
3
3
  # @!group Album
4
+
5
+ # @param [Album] album
6
+ # @return [Boolean] true if the album is populated with data
7
+ # @method album_is_loaded(album)
4
8
  attach_function :album_is_loaded, [ Album ], :bool
9
+
10
+ # @see #album_is_loaded
11
+ # @note the album must be loaded, or this function always return false.
12
+ # @param [Album] album
13
+ # @return [Boolean] true if the album is available for playback
14
+ # @method album_is_available(album)
5
15
  attach_function :album_is_available, [ Album ], :bool
16
+
17
+ # @see #album_is_loaded
18
+ # @note the album must be loaded, or this function always return nil.
19
+ # @param [Album] album
20
+ # @return [Artist, nil] authoring artist of the album
21
+ # @method album_artist(album)
6
22
  attach_function :album_artist, [ Album ], Artist
23
+
24
+ # @see #image_create
25
+ # @see #album_is_loaded
26
+ # @note the album must be loaded, or this function always return nil.
27
+ # @param [Album] album
28
+ # @param [Symbol] image_size one of :normal, :small, :large
29
+ # @return [String, nil] image ID to pass to {#image_create}, or nil if the album has no image
30
+ # @method album_cover(album, image_size)
7
31
  attach_function :album_cover, [ Album, :image_size ], ImageID
32
+
33
+ # @see #album_is_loaded
34
+ # @note the album must be loaded, or this function always return an empty string.
35
+ # @param [Album] album
36
+ # @return [String] name of the album
37
+ # @method album_name(album)
8
38
  attach_function :album_name, [ Album ], UTF8String
39
+
40
+ # @see #album_is_loaded
41
+ # @note the album must be loaded, or this function always return 0.
42
+ # @param [Album] album
43
+ # @return [String] release year of the album
44
+ # @method album_year(album)
9
45
  attach_function :album_year, [ Album ], :int
46
+
47
+ # @see #album_is_loaded
48
+ # @note the album must be loaded, or this function always return :unknown.
49
+ # @param [Album] album
50
+ # @return [Symbol] album type, one of :album, :single, :compilation, :unknown
51
+ # @method album_type(album)
10
52
  attach_function :album_type, [ Album ], :albumtype
11
- attach_function :album_add_ref, [ Album ], :error
12
- attach_function :album_release, [ Album ], :error
53
+
54
+ attach_function :album_add_ref, [ Album ], APIError
55
+ attach_function :album_release, [ Album ], APIError
13
56
  end
14
57
  end