spotify_web 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ccebcb20e1139746afe7ca5f70693c2c945e656
4
+ data.tar.gz: 50db822ca1f7be0f0e3af6b2e5014c25c27e0b58
5
+ SHA512:
6
+ metadata.gz: 00d54b4013292872b94b2e19bce1a1fbb3df5cddb31023803b64bc49a07453986ffc433a7d91964358fe05f5cf01e6e3b2bd34bc69663d5fb802ba54d0de6cd2
7
+ data.tar.gz: f81b8a6a289e16a7ff57042ac867c556cc21ccc3d254457835777deb183762703bcc4ba1914be3be2b614540ae1ba3001e721687d69dd7cd70d6d179f147aea5
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ coverage
5
+ /doc
6
+ /Gemfile.lock
7
+ /log*
8
+ /proto
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,7 @@
1
+ --title "spotify_web"
2
+ --readme README.md
3
+ --no-private
4
+ lib/spotify_web.rb
5
+ lib/spotify_web/resource.rb
6
+ lib/spotify_web/**/*.rb -
7
+ CHANGELOG.md LICENSE
@@ -0,0 +1,5 @@
1
+ # master
2
+
3
+ ## 0.0.1 / 2013-10-26
4
+
5
+ * Initial revision
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://www.rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Aaron Pfeifer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,309 @@
1
+ # spotify_web [![Build Status](https://secure.travis-ci.org/obrie/spotify_web.png "Build Status")](http://travis-ci.org/obrie/spotify_web) [![Dependency Status](https://gemnasium.com/obrie/spotify_web.png "Dependency Status")](https://gemnasium.com/obrie/spotify_web)
2
+
3
+ *spotify_web* is an evented Spotify Web API for Ruby.
4
+
5
+ ## Resources
6
+
7
+ API
8
+
9
+ * http://rdoc.info/github/obrie/spotify_web/master/frames
10
+
11
+ Bugs
12
+
13
+ * http://github.com/obrie/spotify_web/issues
14
+
15
+ Development
16
+
17
+ * http://github.com/obrie/spotify_web
18
+
19
+ Testing
20
+
21
+ * http://travis-ci.org/obrie/spotify_web
22
+
23
+ Source
24
+
25
+ * git://github.com/obrie/spotify_web.git
26
+
27
+ ## Description
28
+
29
+ SpotifyWeb makes it dead-simple to interact with the unofficial Spotify Web API.
30
+ It is an opinionated library that attempts to hide the various complexities of
31
+ the Spotify API by providing a clean design around how data is accessed and
32
+ organized.
33
+
34
+ This project was built from the ground-up by Rubyists for Rubyists. While prior
35
+ projects in other languages were used for guidance on some of the implementation,
36
+ the design is meant to take advantage of the various features offered by Ruby 1.9+.
37
+
38
+ At a high level, this project features:
39
+
40
+ * Evented, non-blocking I/O
41
+ * Fiber-aware, untangled callbacks
42
+ * Interactive console support
43
+ * Clean, object-oriented APIs
44
+ * Detailed API documentation
45
+ * Lazy-loaded attributes
46
+ * Auto-reconnects for bots
47
+ * Consistent API / attribute naming schemes
48
+ * DSL syntax support
49
+
50
+ SpotifyWeb features include access to / management of:
51
+
52
+ * User playlists
53
+ * Artist / Album / Song metadata
54
+ * Others to be added...
55
+
56
+ Examples of the usage patterns for some of the above features are shown below.
57
+ You can find much more detailed documentation in the actual API.
58
+
59
+ ## Usage
60
+
61
+ ### Example
62
+
63
+ Below is an example of some of the features offered by this API:
64
+
65
+ ```ruby
66
+ require 'spotify_web'
67
+
68
+ USERNAME = ENV['USERNAME']
69
+ PASSWORD = ENV['PASSWORD']
70
+
71
+ SpotifyWeb.run do
72
+ client = SpotifyWeb::Client.new(USERNAME, PASSWORD)
73
+
74
+ # Events
75
+ client.on :session_authenticated do
76
+ # ...
77
+ end
78
+
79
+ # Authorized user interactions
80
+ user = client.user # => #<SpotifyWeb::AuthorizedUser @username="benzelano" ...>
81
+
82
+ # Playlist interaction
83
+ user.playlists # => [#<SpotifyWeb::Playlist @uri="hm://playlist/user/..." ...>, ...]
84
+ user.playlist(:starred).songs.each do
85
+ song.artist.name
86
+ song.artist.albums
87
+ song.artist.top_songs
88
+ song.title
89
+ song.album.published_on
90
+ end
91
+ end
92
+ ```
93
+
94
+ The example above is just a very, very small subset of the possible things you
95
+ can do with spotify_web. For a *complete* list, see the API documentation, especially:
96
+
97
+ * [SpotifyWeb::Album](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/Album)
98
+ * [SpotifyWeb::Artist](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/Artist)
99
+ * [SpotifyWeb::AuthorizedUser](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/AuthorizedUser)
100
+ * [SpotifyWeb::Client](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/Client)
101
+ * [SpotifyWeb::Playlist](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/Playlist)
102
+ * [SpotifyWeb::Song](http://rdoc.info/github/obrie/spotify_web/master/frames/SpotifyWeb/Song)
103
+
104
+ For additional examples, see the [examples](https://github.com/obrie/spotify_web/tree/master/examples)
105
+ directory in the repository.
106
+
107
+ ## Additional Topics
108
+
109
+ ### Differences with existing libraries
110
+
111
+ So you may be asking "Why re-build this in Ruby when you have a stable
112
+ Javascript project?" There are two main reasons. First, one of the projects
113
+ that need this was in Ruby and preferred to have a Ruby interface to the APi.
114
+ Second, I felt that many of the high-level details highlighted in the
115
+ Description section of this document were missing in existing libraries.
116
+
117
+ Some of those details include untangled callbacks, object-oriented APIs,
118
+ external API consistency, auto lazy-loading, etc. This library also strives
119
+ to be a complete implementation and easy to use / play around with.
120
+
121
+ By no means does this discredit the significance and usefulness of the other
122
+ libraries -- they all have a user and all provided the foundation necessary
123
+ to build out this project.
124
+
125
+ ### Authentication
126
+
127
+ spotify_web authenticates users with the username and password associated with
128
+ their account. For example:
129
+
130
+ ```ruby
131
+ SpotifyWeb.run do
132
+ client = SpotifyWeb::Client.new(USERNAME, PASSWORD)
133
+ # ...
134
+ end
135
+ ```
136
+
137
+ ### Interactive Console
138
+
139
+ Typically it's difficult to debug or run simple tests within IRB when using
140
+ [EventMachine](http://rubyeventmachine.com/). However, spotify_web provides a
141
+ few simple ways to do this so that you can play around with the API interactively.
142
+
143
+ For example:
144
+
145
+ ```ruby
146
+ 1.9.3-p286 :001 > require 'spotify_web'
147
+ => true
148
+ 1.9.3-p286 :002 > SpotifyWeb.interactive
149
+ => true
150
+ 1.9.3-p286 :003 > client = nil
151
+ => nil
152
+ 1.9.3-p286 :004 > SpotifyWeb.run do
153
+ 1.9.3-p286 :005 > client = SpotifyWeb::Client.new(USERAME, PASSWORD)
154
+ 1.9.3-p286 :006 > end
155
+ => nil
156
+
157
+ # later on...
158
+ 1.9.3-p286 :008 > SpotifyWeb.run { puts client.playlists.inspect }
159
+ => nil
160
+ [#<SpotifyWeb::Playlist:0xa0c7da8 @uri="...">, #<SpotifyWeb::Playlist:0xa0c7bf0 @uri="...">]
161
+ ```
162
+
163
+ In this example, an instance of `SpotifyWeb::Client` is created and tracked in
164
+ the console. Later on, we can then run a command on that client by evaluating
165
+ it within a `SpotifyWeb.run` block.
166
+
167
+ ### DSL syntax
168
+
169
+ spotify_web has basic support for a DSL language in order to simplify some of the
170
+ scripts you may be writing. The DSL is essentially made available by executing
171
+ blocks within the context of a `SpotifyWeb::Client` instance.
172
+
173
+ There are two ways to do this:
174
+
175
+ ```ruby
176
+ # Using the SpotifyWeb.run shortcut:
177
+
178
+ SpotifyWeb.run(USERNAME, PASSWORD) do
179
+ playlists.each do
180
+ # ...
181
+ end
182
+ on :session_authenticated do
183
+ # ...
184
+ end
185
+ end
186
+
187
+ # Passing a block into SpotifyWeb::Client:
188
+
189
+ SpotifyWeb.run do
190
+ SpotifyWeb::Client.new(USERNAME, PASSWORD) do
191
+ playlists.each do
192
+ # ...
193
+ end
194
+ on :session_authenticated do
195
+ # ...
196
+ end
197
+ end
198
+ end
199
+ ```
200
+
201
+ *Note* that you will likely not want to use the first example (using the
202
+ `SpotifyWeb.run` shortcut) when running in the context of a web request in a
203
+ web server, simply because it will start a new Fiber.
204
+
205
+ The equivalent, non-DSL example looks like so:
206
+
207
+ ```ruby
208
+ SpotifyWeb.run do
209
+ client = SpotifyWeb::Client.new(USERNAME, PASSWORD)
210
+ client.playlists.each do
211
+ # ...
212
+ end
213
+ client.on :session_authenticated do
214
+ # ...
215
+ end
216
+ end
217
+ ```
218
+
219
+ Notice that in this example the syntax is essentially the same except that we're
220
+ one level out and need to interact directly with the `SpotifyWeb::Client`
221
+ instance itself.
222
+
223
+ ## Deployment
224
+
225
+ ### Web Server Usage
226
+
227
+ You'll notice that in many places in the documentation, `SpotifyWeb.run` is
228
+ used to start running a block of code for interacting with the API. This is
229
+ done in order to ensure that the block of code is being run with a running
230
+ EventMachine and within a non-root Fiber.
231
+
232
+ When spotify_web is being used as part of a web server or anything else that's
233
+ already running EventMachine and already executing code within a non-root Fiber
234
+ (such as the rainbows web server) you *should not* using the `run` API. Instead
235
+ you can just run your block like normal:
236
+
237
+ ```ruby
238
+ client = SpotifyWeb::Client.new(USERNAME, PASSWORD)
239
+ playlists = client.user.playlists
240
+ # ...
241
+ ```
242
+
243
+ ### Persistent Usage
244
+
245
+ If you're using spotify_web for persistence, long-lived usage, the primary thing to keep
246
+ in mind is how to handle connection loss. This can occur as a result of a lost
247
+ internet connection or even just Spotify forcefully closing a socket for unknown
248
+ reasons. To protect against this, you can configure spotify_web to automatically
249
+ keep attempting to re-open a connection when it's been closed.
250
+
251
+ For example:
252
+
253
+ ```ruby
254
+ SpotifyWeb.run(USERNAME, PASSWORD, :reconnect => true, :reconnect_wait => 60) do
255
+ # ...
256
+ end
257
+ ```
258
+
259
+ In this example, spotify_web will automatically attempt to reconnect if the socket
260
+ is ever closed by reasons other than you closing it yourself. However, rather
261
+ than constantly trying to hit Spotify's servers you can configure a reconnect
262
+ wait timeout that will cause spotify_web to wait a certain number of seconds before
263
+ attempting to open a connection. This will continue to happen until the connection
264
+ is successful.
265
+
266
+ ## Testing
267
+
268
+ To run the core test suite:
269
+
270
+ ```bash
271
+ bundle install
272
+ bundle exec rspec
273
+ ```
274
+
275
+ ## Caveats
276
+
277
+ The following caveats should be noted when using spotify_web:
278
+
279
+ * Since this library uses EventMachine / Fibers it will only be compatible with
280
+ web servers that support those technologies. Examples of such web servers include:
281
+ * [Thin](http://code.macournoyer.com/thin/)
282
+ * [Rainbows](http://rainbows.rubyforge.org/)
283
+ * [Goliath](http://postrank-labs.github.com/goliath/)
284
+ * This is *not* an official library and so Spotify may make changes to its API
285
+ that causes this to break. Hopefully we can build a community that can quickly
286
+ react and provide fixes to those changes.
287
+
288
+ ## Things to do
289
+
290
+ * Add test coverage
291
+ * 100% complete Spotify Web API implementation
292
+
293
+ ## Contributions
294
+
295
+ The largest contribution for this library is the reference material provided by
296
+ Nathan Rajlich's [node-spotify-web](https://github.com/TooTallNate/node-spotify-web)
297
+ and Hexxeh's [spotify-websocket-api](github.com/Hexxeh/spotify-websocket-api) libraries.
298
+ They provided much of the legwork to get understand how Spotify's Web API works and
299
+ made it much easier to bring a Ruby perspective to it.
300
+
301
+ ## Dependencies
302
+
303
+ * Ruby 1.9.3 or later
304
+ * [beefcake](https://github.com/protobuf-ruby/beefcake)
305
+ * [em-http-request](https://github.com/igrigorik/em-http-request)
306
+ * [em-synchrony](https://github.com/igrigorik/em-synchrony)
307
+ * [execjs](https://github.com/sstephenson/execjs)
308
+ * [faye-websocket-ruby](https://github.com/faye/faye-websocket-ruby)
309
+ * [radix](https://github.com/rubyworks/radix)
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'rake'
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ desc 'Default: run all specs.'
11
+ task :default => :spec
12
+
13
+ load File.dirname(__FILE__) + '/lib/tasks/spotify_web.rake'
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # List all songs in the user's playlists
3
+ require 'spotify_web'
4
+
5
+ USERNAME = ENV['USERNAME'] # 'xxxxx'
6
+ PASSWORD = ENV['PASSWORD'] # 'xxxxx'
7
+
8
+ SpotifyWeb.run(EMAIL, PASSWORD) do
9
+ user.playlists.each do |playlist|
10
+ playlist.songs.each do |song|
11
+ puts "[#{playlist.name}] #{song.artist.name} - #{song.title}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,105 @@
1
+ require 'logger'
2
+ require 'em-synchrony'
3
+ require 'em-synchrony/em-http'
4
+
5
+ # Spotify Web API for Ruby
6
+ module SpotifyWeb
7
+ # The user agent for Spotify access
8
+ USER_AGENT = 'node-spotify-web (Chrome/13.37 compatible-ish)'
9
+
10
+ autoload :Client, 'spotify_web/client'
11
+ autoload :Schema, 'spotify_web/schema'
12
+
13
+ class << self
14
+ # The logger to use for all Spotify messages. By default, everything is
15
+ # logged to STDOUT.
16
+ # @return [Logger]
17
+ attr_accessor :logger
18
+
19
+ # Whether this is going to be used in an interactive console such as IRB.
20
+ # If this is enabled then EventMachine will run in a separate thread. This
21
+ # will allow IRB to continue to actually be interactive.
22
+ #
23
+ # @note You must continue to run all commands on a client through SpotifyWeb#run.
24
+ # @example
25
+ # require 'spotify_web'
26
+ #
27
+ # SpotifyWeb.interactive
28
+ # SpotifyWeb.run do
29
+ # @client = SpotifyWeb::Client.new(...)
30
+ # @client.start
31
+ # end
32
+ #
33
+ # # ...later on after the connection has started and you want to interact with it
34
+ # SpotifyWeb.run do
35
+ # @client.user.playlists
36
+ # # ...
37
+ # end
38
+ def interactive
39
+ Thread.new { EM.run }.abort_on_exception = true
40
+ end
41
+
42
+ # Sets up the proper EventMachine reactor / Fiber to run commands against a
43
+ # client. If this is not in interactive mode, then the block won't return
44
+ # until the EventMachine reactor is stopped.
45
+ #
46
+ # @note If you're already running within an EventMachine reactor *and* a
47
+ # Fiber, then there's no need to call this method prior to interacting with
48
+ # a SpotifyWeb::Client instance.
49
+ # @example
50
+ # # Non-interactive, not in reactor / fiber
51
+ # SpotifyWeb.run do
52
+ # client = SpotifyWeb::Client.new(...)
53
+ # client.playlists
54
+ # # ...
55
+ # end
56
+ #
57
+ # # Interactive, not in reactor / fiber
58
+ # SpotifyWeb.interactive
59
+ # SpotifyWeb.run do
60
+ # @client = ...
61
+ # end
62
+ # SpotifyWeb.run do
63
+ # @client.playlists
64
+ # # ...
65
+ # end
66
+ #
67
+ # # Non-interactive, already in reactor / fiber
68
+ # client = SpotifyWeb::Client(...)
69
+ # client.playlists
70
+ #
71
+ # @example DSL
72
+ # # Takes the same arguments as SpotifyWeb::Client
73
+ # SpotifyWeb.run(USERNAME, PASSWORD) do
74
+ # user.playlists
75
+ # end
76
+ #
77
+ # == Exception handling
78
+ #
79
+ # Any exceptions that occur within the block will be automatically caught
80
+ # and logged. This prevents the EventMachine reactor from dying.
81
+ def run(*args, &block)
82
+ if EM.reactor_running?
83
+ EM.next_tick do
84
+ EM.synchrony do
85
+ begin
86
+ if args.any?
87
+ # Run the block within a client
88
+ Client.new(*args, &block)
89
+ else
90
+ # Just run the block within a fiber
91
+ block.call
92
+ end
93
+ rescue StandardError => ex
94
+ logger.error(([ex.message] + ex.backtrace) * "\n")
95
+ end
96
+ end
97
+ end
98
+ else
99
+ EM.synchrony { run(*args, &block) }
100
+ end
101
+ end
102
+ end
103
+
104
+ @logger = Logger.new(STDOUT)
105
+ end