spotify_web 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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