jberkel-spotify-api 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.
- data/.gitignore +1 -0
- data/LICENSE +24 -0
- data/README.md +91 -0
- data/Rakefile +33 -0
- data/VERSION.yml +4 -0
- data/bin/spotify-api-server +25 -0
- data/examples/create_playlist.rb +24 -0
- data/examples/list_playlists.rb +16 -0
- data/examples/search.rb +16 -0
- data/lib/jars/jotify.jar +0 -0
- data/lib/jotify.rb +109 -0
- data/lib/jotify/api.rb +114 -0
- data/lib/jotify/media.rb +99 -0
- data/spec/api_spec.rb +179 -0
- data/spec/integration_spec.rb +33 -0
- data/spec/jotify_spec.rb +32 -0
- data/spec/media_spec.rb +77 -0
- data/spec/spec_helper.rb +11 -0
- data/spotify-api.gemspec +76 -0
- metadata +119 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2009, Jan Berkel
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of spotify-api nor the
|
12
|
+
names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY Jan Berkel 'AS IS'' AND ANY
|
16
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL David R. MacIver BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
# Spotify-API
|
3
|
+
|
4
|
+
I got tired of waiting for Spotify to release their API (if they release it), so decided to roll my own based on the awesome
|
5
|
+
work done by [#hack.se](http://despotify.se) (despotify) and Felix Bruns who created a Java port of despotify called
|
6
|
+
[jotify](http://jotify.felixbruns.de/). Add JRuby, Sinatra and some glue to the mix and you (almost) have an API.
|
7
|
+
|
8
|
+
*Important*: in order to use this API you'll need a premium spotify account! This API is as unofficial as it gets and *NOT* supported
|
9
|
+
by Spotify in any way.
|
10
|
+
|
11
|
+
At the moment the following features are implemented:
|
12
|
+
|
13
|
+
* searching [GET /(albums|tracks|artists)?name=Foo]
|
14
|
+
* list user's playlists [GET /playlists]
|
15
|
+
* get shared playlist [GET /playlist/id]
|
16
|
+
* create new playlist [POST /playlists]
|
17
|
+
* add tracks to playlist [PUT /playlists/id]
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Prerequisites: *Java 6+*, JRuby 1.3.x.
|
22
|
+
|
23
|
+
$ jruby -S gem install jberkel-spotify-api --source http://gems.github.com
|
24
|
+
$ jruby -S spotify-api-server --account login:password
|
25
|
+
== Sinatra/0.9.4 has taken the stage on 3000 for development with backup from WEBrick
|
26
|
+
[2009-08-04 01:21:03] INFO WEBrick 1.3.1
|
27
|
+
[2009-08-04 01:21:03] INFO ruby 1.8.6 (2009-07-24) [java]
|
28
|
+
[2009-08-04 01:21:03] INFO WEBrick::HTTPServer#start: pid=12162 port=3000
|
29
|
+
|
30
|
+
$ curl http://localhost:3000/playlists | jsonpretty
|
31
|
+
{
|
32
|
+
"result": {
|
33
|
+
"playlists": [
|
34
|
+
{
|
35
|
+
"name": "my shiny playlist",
|
36
|
+
"author": "jberkel",
|
37
|
+
"url": "http:\/\/open.spotify.com\/user\/jberkel\/playlist\/5EXLGE7HPVPjvlxPmIfrDe",
|
38
|
+
"revision": 2,
|
39
|
+
"id": "b9fe3dcf88945d146ef18117faa61ab4",
|
40
|
+
"tracks": [
|
41
|
+
{
|
42
|
+
"artist": "Elmore Judd",
|
43
|
+
"title": "Disco In 4 Pieces",
|
44
|
+
"url": "http:\/\/open.spotify.com\/track\/1VaucR6Bsks5Q9bYBsXvuF",
|
45
|
+
"id": "3f2752a98dd947c5855278a88159d7b1",
|
46
|
+
"album": "Insect Funk",
|
47
|
+
"popularity": 0.325379997491837
|
48
|
+
},
|
49
|
+
{
|
50
|
+
"artist": "40winks",
|
51
|
+
"title": "Goodmorning (intro)",
|
52
|
+
"url": "http:\/\/open.spotify.com\/track\/6qHiOf1BFCQIzAjJsRbMfY",
|
53
|
+
"id": "d34a3a6daeed4f93983068e4e8c26cd6",
|
54
|
+
"album": "Sound Puzzle",
|
55
|
+
"popularity": 0.170609995722771
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"collaborative": false
|
59
|
+
}
|
60
|
+
]
|
61
|
+
},
|
62
|
+
"status": "OK"
|
63
|
+
}
|
64
|
+
|
65
|
+
See examples directory for usage.
|
66
|
+
|
67
|
+
## Credits
|
68
|
+
|
69
|
+
Contains code from the jotify project:
|
70
|
+
|
71
|
+
Copyright (c) 2009, Felix Bruns <felixbruns@web.de>
|
72
|
+
All rights reserved.
|
73
|
+
|
74
|
+
Redistribution and use in source and binary forms, with or without
|
75
|
+
modification, are permitted provided that the following conditions are met:
|
76
|
+
* Redistributions of source code must retain the above copyright
|
77
|
+
notice, this list of conditions and the following disclaimer.
|
78
|
+
* Redistributions in binary form must reproduce the above copyright
|
79
|
+
notice, this list of conditions and the following disclaimer in the
|
80
|
+
documentation and/or other materials provided with the distribution.
|
81
|
+
|
82
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
|
83
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
84
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
85
|
+
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
86
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
87
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
88
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
89
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
90
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
91
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "spotify-api"
|
9
|
+
gem.summary = "an api for spotify, based on jotify"
|
10
|
+
gem.email = "jan.berkel@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/jberkel/spotify-api"
|
12
|
+
gem.description = "an api for spotify, based on jotify"
|
13
|
+
gem.authors = ["Jan Berkel"]
|
14
|
+
gem.add_dependency "rack"
|
15
|
+
gem.add_dependency "rack-test"
|
16
|
+
gem.add_dependency "sinatra"
|
17
|
+
gem.add_dependency "json-jruby"
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
21
|
+
end
|
22
|
+
|
23
|
+
Spec::Rake::SpecTask.new do |t|
|
24
|
+
t.rcov = false
|
25
|
+
t.spec_files = FileList["spec/**/*_spec.rb"].delete_if { |f| f =~ /integration/ }
|
26
|
+
t.libs << "./lib"
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:integration) do |t|
|
30
|
+
t.rcov = false
|
31
|
+
t.spec_files = FileList["spec/**/*_spec.rb"].select { |f| f =~ /integration/ }
|
32
|
+
t.libs << "./lib"
|
33
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'jotify'
|
6
|
+
require 'jotify/api'
|
7
|
+
|
8
|
+
DEFAULT_PORT = port = 3000
|
9
|
+
|
10
|
+
def usage
|
11
|
+
STDERR.puts "#{File.basename($0)} [-p <port>] [--account <login>:<password>]"
|
12
|
+
exit(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
case ARGV.shift
|
16
|
+
when '-p', '--port': port = ARGV.shift
|
17
|
+
when '--account':
|
18
|
+
login, password = ARGV.shift.to_s.split(':')
|
19
|
+
raise ArgumentError, "you need to specify both login and password!" unless login and password
|
20
|
+
Jotify.credentials = { :username=>login, :password=>password }
|
21
|
+
when '-h', '--help': usage
|
22
|
+
end
|
23
|
+
|
24
|
+
#Sinatra::Application.set :environment, :production
|
25
|
+
Sinatra::Application.run! :port=> (port || DEFAULT_PORT).to_i
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'json'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
puts "creating a new playlist"
|
9
|
+
resp = RestClient.post 'http://localhost:3000/playlists', :name=>'my shiny playlist'
|
10
|
+
if resp.code == 201
|
11
|
+
location = resp.headers[:location]
|
12
|
+
puts "201 created (#{location})"
|
13
|
+
spotify_id = location[location.rindex('/')+1..-1]
|
14
|
+
|
15
|
+
puts "adding some tracks to it"
|
16
|
+
resp = RestClient.put "http://localhost:3000/playlists/#{spotify_id}", {
|
17
|
+
"tracks" => [ {'id'=>'6qHiOf1BFCQIzAjJsRbMfY'}, {'id'=>'1VaucR6Bsks5Q9bYBsXvuF'} ]
|
18
|
+
}.to_json
|
19
|
+
puts resp
|
20
|
+
else
|
21
|
+
raise resp.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'json'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
puts "getting all playlists"
|
9
|
+
resp = RestClient.get 'http://localhost:3000/playlists'
|
10
|
+
playlists = JSON.parse(resp)
|
11
|
+
pp playlists
|
12
|
+
|
13
|
+
playlists['result']['playlists'].each do |p|
|
14
|
+
puts "retrieving details for playlist #{p['name']}:"
|
15
|
+
pp JSON.parse(RestClient.get "http://localhost:3000/playlists/#{p['id']}")
|
16
|
+
end
|
data/examples/search.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'json'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
puts "searching for album: Moon Safari"
|
9
|
+
pp JSON.parse(RestClient.get('http://localhost:3000/albums?name=Moon+Safari'))
|
10
|
+
|
11
|
+
puts "searching for track: The End"
|
12
|
+
pp JSON.parse(RestClient.get('http://localhost:3000/tracks?name=The+End'))
|
13
|
+
|
14
|
+
puts "searching for artist: The Doors"
|
15
|
+
pp JSON.parse(RestClient.get('http://localhost:3000/artists?name=The+Doors'))
|
16
|
+
|
data/lib/jars/jotify.jar
ADDED
Binary file
|
data/lib/jotify.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'java'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/jars/jotify.jar')
|
3
|
+
|
4
|
+
class Jotify
|
5
|
+
|
6
|
+
module Media
|
7
|
+
import 'de.felixbruns.jotify.media.Playlist'
|
8
|
+
import 'de.felixbruns.jotify.media.PlaylistContainer'
|
9
|
+
import 'de.felixbruns.jotify.media.Result'
|
10
|
+
import 'de.felixbruns.jotify.media.Track'
|
11
|
+
import 'de.felixbruns.jotify.media.Artist'
|
12
|
+
import 'de.felixbruns.jotify.media.Album'
|
13
|
+
end
|
14
|
+
|
15
|
+
import 'de.felixbruns.jotify.gui.util.JotifyPreferences'
|
16
|
+
import 'de.felixbruns.jotify.util.SpotifyURI'
|
17
|
+
|
18
|
+
ByPopularity = Proc.new { |a,b| b.popularity <=> a.popularity }
|
19
|
+
|
20
|
+
[:close, :search].each do |m|
|
21
|
+
define_method(m) do |*args|
|
22
|
+
@jotify.send(m, *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(jotify_impl=Java::DeFelixbrunsJotify::JotifyPool.new(4))
|
27
|
+
@jotify = jotify_impl
|
28
|
+
|
29
|
+
credentials = Jotify.credentials
|
30
|
+
@jotify.login(credentials[:username], credentials[:password])
|
31
|
+
|
32
|
+
at_exit do
|
33
|
+
begin
|
34
|
+
@jotify.close
|
35
|
+
rescue Exception => e
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if block_given?
|
40
|
+
begin
|
41
|
+
yield self
|
42
|
+
ensure
|
43
|
+
close
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def playlists
|
49
|
+
@jotify.playlists.map { |p| playlist(p.getId()) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def playlist(id)
|
53
|
+
playlist = @jotify.playlist(Jotify.resolve_id(id))
|
54
|
+
unless playlist.tracks.empty?
|
55
|
+
res = @jotify.browse(playlist.tracks)
|
56
|
+
res.tracks.each_with_index do |t,i|
|
57
|
+
playlist.tracks.set(i, t)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
playlist
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_playlist(name, collaborative=false)
|
64
|
+
playlist = @jotify.playlistCreate(name, collaborative)
|
65
|
+
return nil unless playlist
|
66
|
+
add_playlist(playlist)
|
67
|
+
playlist
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_playlist(id)
|
71
|
+
@jotify.playlistsAddPlaylist(@jotify.playlists, id.is_a?(Media::Playlist) ? id : playlist(id))
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_tracks_to_playlist(playlist, track_ids)
|
75
|
+
tracks = Java::JavaUtil::ArrayList.new
|
76
|
+
track_ids.each { |id| tracks.add(Media::Track.new(Jotify.resolve_id(id))) }
|
77
|
+
@jotify.playlistAddTracks(playlist, tracks, 0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.resolve_id(id)
|
81
|
+
case id
|
82
|
+
when /\Ahttp:\/\/open\.spotify\.com/: SpotifyURI.to_hex(id[id.rindex('/')+1..-1])
|
83
|
+
when /spotify:/: SpotifyURI.to_hex(id[id.rindex(':')+1..-1])
|
84
|
+
when /\A[0-9a-f]{32}\Z/: id
|
85
|
+
when /\A[a-zA-Z0-9]{22}\Z/: SpotifyURI.to_hex(id)
|
86
|
+
else
|
87
|
+
raise "invalid id: #{id}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.credentials
|
92
|
+
prefs = JotifyPreferences.getInstance()
|
93
|
+
prefs.load()
|
94
|
+
{
|
95
|
+
:username => prefs.getString("login.username"),
|
96
|
+
:password => prefs.getString("login.password")
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.credentials=(creds)
|
101
|
+
prefs = JotifyPreferences.getInstance()
|
102
|
+
prefs.load()
|
103
|
+
prefs.setString("login.username", creds[:username])
|
104
|
+
prefs.setString("login.password", creds[:password])
|
105
|
+
prefs.save() or raise "could not save login details"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
require File.expand_path(File.dirname(__FILE__) + '/jotify/media')
|
data/lib/jotify/api.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'json'
|
4
|
+
require 'sinatra/base'
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/../jotify') unless defined?(Jotify)
|
6
|
+
|
7
|
+
class Sinatra::Application
|
8
|
+
Lock = Mutex.new
|
9
|
+
|
10
|
+
def jotify
|
11
|
+
Lock.synchronize do
|
12
|
+
@@jotify ||= Jotify.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def query(what, p=what)
|
17
|
+
params[p] ? "#{what}:" + params[p] : ''
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sinatra::Application.error ArgumentError do
|
22
|
+
content_type :json
|
23
|
+
{
|
24
|
+
'status' => 'ERROR',
|
25
|
+
'message' => request.env['sinatra.error'].message
|
26
|
+
}.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
Sinatra::Application.get('/tracks') do
|
30
|
+
content_type :json
|
31
|
+
raise ArgumentError, "need name" unless params[:name]
|
32
|
+
|
33
|
+
res = jotify.search([query(:track, :name), query(:artist), query(:album)].join(' '))
|
34
|
+
{
|
35
|
+
'status'=>'OK',
|
36
|
+
'result'=> res.tracks.map { |t| t.to_h }
|
37
|
+
}.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
Sinatra::Application.get('/albums') do
|
41
|
+
content_type :json
|
42
|
+
raise ArgumentError, "need name" unless params[:name]
|
43
|
+
|
44
|
+
res = jotify.search([query(:album, :name), query(:artist)].join(' ') )
|
45
|
+
{
|
46
|
+
'status'=>'OK',
|
47
|
+
'result'=> res.albums.map { |a| a.to_h }
|
48
|
+
}.to_json
|
49
|
+
end
|
50
|
+
|
51
|
+
Sinatra::Application.get('/artists') do
|
52
|
+
content_type :json
|
53
|
+
raise ArgumentError, "need name" unless params[:name]
|
54
|
+
|
55
|
+
res = jotify.search(query(:artist, :name))
|
56
|
+
{
|
57
|
+
'status'=>'OK',
|
58
|
+
'result'=> res.artists.map { |a| a.to_h }
|
59
|
+
}.to_json
|
60
|
+
end
|
61
|
+
|
62
|
+
Sinatra::Application.get('/playlists') do
|
63
|
+
content_type :json
|
64
|
+
#playlists = jotify.playlists
|
65
|
+
{
|
66
|
+
'status'=>'OK',
|
67
|
+
'result'=> { 'playlists' => jotify.playlists.map { |p| p.to_h } }
|
68
|
+
}.to_json
|
69
|
+
end
|
70
|
+
|
71
|
+
Sinatra::Application.get('/playlists/:id') do
|
72
|
+
content_type :json
|
73
|
+
if playlist = jotify.playlist(params[:id])
|
74
|
+
{
|
75
|
+
'status'=>'OK',
|
76
|
+
'result'=>playlist.to_h
|
77
|
+
}.to_json
|
78
|
+
else
|
79
|
+
return 404, { 'status' => 'ERROR', 'message' => 'playlist not found' }.to_json
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Sinatra::Application.post('/playlists') do
|
84
|
+
content_type :json
|
85
|
+
name, collaborative = params[:name], params[:collaborative] == 'true'
|
86
|
+
playlist = jotify.create_playlist(name, collaborative)
|
87
|
+
if playlist
|
88
|
+
redirect playlist.link, 201 # created
|
89
|
+
else
|
90
|
+
return 500, { 'status' => 'ERROR', 'message' => 'playlist could not be created' }.to_json
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Sinatra::Application.put('/playlists/:id') do
|
95
|
+
content_type :json
|
96
|
+
playlist = jotify.playlist(params[:id])
|
97
|
+
return 404, { 'status' => 'ERROR', 'message' => 'playlist not found' }.to_json unless playlist
|
98
|
+
body = request.body.read
|
99
|
+
data = JSON.parse(body)
|
100
|
+
raise ArgumentError, "invalid format" unless data.is_a?(Hash) && data['tracks'].is_a?(Array)
|
101
|
+
ids = data['tracks'].map { |t| t['id'] }
|
102
|
+
return 200, { 'status' => 'OK', 'message' => 'not modified' }.to_json if ids.empty?
|
103
|
+
|
104
|
+
if jotify.add_tracks_to_playlist(playlist, ids)
|
105
|
+
return 200, { 'status' => 'OK', 'message' => "successfully added #{ids.size} tracks" }.to_json
|
106
|
+
else
|
107
|
+
return 500, { 'status' => 'ERROR', 'message' => 'could not add to playlist' }.to_json
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
if __FILE__ == $0
|
113
|
+
Sinatra::Application.run!
|
114
|
+
end
|
data/lib/jotify/media.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'java'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../jars/jotify.jar')
|
3
|
+
|
4
|
+
#Make Jotify's native Java classes a bit more rubyish by implementing some mehods / modules
|
5
|
+
module Java
|
6
|
+
module DeFelixbrunsJotifyMedia
|
7
|
+
class Media
|
8
|
+
def inspect
|
9
|
+
self.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
h = { :id=>self.getId(), :popularity=> popularity.nan? ? 0.0 : popularity.to_f }
|
14
|
+
h[:url] = self.link if self.respond_to?(:link)
|
15
|
+
h
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Playlist
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
def each(&block)
|
23
|
+
tracks.each(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"[Playlist: #{self.getId()} #{getTracks.to_a}]"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
{
|
32
|
+
:id => getId(),
|
33
|
+
:name=> name,
|
34
|
+
:url => link,
|
35
|
+
:tracks => tracks.map { |t| t.to_h },
|
36
|
+
:author => author,
|
37
|
+
:revision => revision,
|
38
|
+
:collaborative => collaborative
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class PlaylistContainer
|
44
|
+
include Enumerable
|
45
|
+
|
46
|
+
def each(&block)
|
47
|
+
playlists.each(&block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Result
|
52
|
+
include Enumerable
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
artists.each(&block)
|
56
|
+
tracks.each(&block)
|
57
|
+
albums.each(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
{ :artists=>self.artists.to_a, :albums=>self.albums.to_a, :tracks=>self.tracks.to_a }.inspect
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_h
|
65
|
+
{
|
66
|
+
:artists => self.artists.map { |a| a.to_h },
|
67
|
+
:albums => self.albums.map { |a| a.to_h },
|
68
|
+
:tracks => self.tracks.map { |t| t.to_h }
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Track
|
74
|
+
def to_h
|
75
|
+
super.merge(:title=>title, :artist=>artist ? artist.name : nil, :album=>album ? album.name : nil)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Artist
|
80
|
+
def to_h
|
81
|
+
super.merge(:name=>name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Album
|
86
|
+
include Enumerable
|
87
|
+
|
88
|
+
def each(&block)
|
89
|
+
tracks.each(&block)
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_h
|
93
|
+
super.merge(:name=>name, :artist=>artist ? artist.name : nil, :year=>year, :type=>type)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
#!/usr/bin/env jruby -S spec
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
require 'jotify/api'
|
5
|
+
require 'rack/test'
|
6
|
+
require 'sinatra'
|
7
|
+
|
8
|
+
set :environment, :test
|
9
|
+
|
10
|
+
describe 'Api' do
|
11
|
+
|
12
|
+
def app() Sinatra::Application end
|
13
|
+
|
14
|
+
def json_response
|
15
|
+
last_response.content_type.should == 'application/json'
|
16
|
+
JSON.parse(last_response.body)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
before do
|
21
|
+
@jotify = jotify = mock('Jotify')
|
22
|
+
Sinatra::Application.send(:define_method, :jotify) { jotify }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "/artists" do
|
26
|
+
it "searches by artist name" do
|
27
|
+
res = Jotify::Media::Result.new
|
28
|
+
res.artists.add(Jotify::Media::Artist.new('4d921ebcdd8c80f32ce1ed5acafbb9c8', 'The Kinks'))
|
29
|
+
@jotify.stub!(:search).and_return(res)
|
30
|
+
get '/artists', :name=>'The Kinks'
|
31
|
+
last_response.should be_ok
|
32
|
+
json_response.should == {"status"=>"OK", "result"=>
|
33
|
+
[{"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8", "popularity"=>0.0, "url"=>"http://open.spotify.com/artist/2mnbxTkghYtlHMdX3jdP9C", "name"=>"The Kinks"}]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return an error message if no name is specified" do
|
38
|
+
lambda { get '/artists' }.should raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "/tracks" do
|
43
|
+
it "searches by track name" do
|
44
|
+
res = Jotify::Media::Result.new
|
45
|
+
res.tracks.add(Jotify::Media::Track.new('4d921ebcdd8c80f32ce1ed5acafbb9c8'))
|
46
|
+
@jotify.stub!(:search).and_return(res)
|
47
|
+
get '/tracks', :name=>'Waterloo Sunset'
|
48
|
+
last_response.should be_ok
|
49
|
+
json_response.should == {"status"=>"OK", "result"=>
|
50
|
+
[{"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8", "popularity"=>0.0, "url"=>"http://open.spotify.com/track/2mnbxTkghYtlHMdX3jdP9C", "title"=>nil, "artist"=>nil, "album"=>nil}]
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return an error message if no name is specified" do
|
55
|
+
lambda { get '/tracks' }.should raise_error(ArgumentError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "/albums" do
|
60
|
+
it "searches by album name" do
|
61
|
+
res = Jotify::Media::Result.new
|
62
|
+
res.albums.add(Jotify::Media::Album.new('4d921ebcdd8c80f32ce1ed5acafbb9c8',
|
63
|
+
'Something Else',
|
64
|
+
Jotify::Media::Artist.new('4d921ebcdd8c80f32ce1ed5acafbb9c8', 'The Kinks')))
|
65
|
+
|
66
|
+
@jotify.stub!(:search).and_return(res)
|
67
|
+
get '/albums', :name=>'Something Else'
|
68
|
+
last_response.should be_ok
|
69
|
+
json_response.should == {"status"=>"OK", "result"=>
|
70
|
+
[{"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
71
|
+
"popularity"=>0.0,
|
72
|
+
"url"=>"http://open.spotify.com/album/2mnbxTkghYtlHMdX3jdP9C",
|
73
|
+
"name"=>"Something Else",
|
74
|
+
"artist"=>"The Kinks", "year"=>-1, "type"=>nil}]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return an error message if no name is specified" do
|
79
|
+
lambda { get '/albums' }.should raise_error(ArgumentError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "/playlists" do
|
84
|
+
before do
|
85
|
+
#String id, String name, String author, boolean collaborative
|
86
|
+
@playlist = Jotify::Media::Playlist.new("4d921ebcdd8c80f32ce1ed5acafbb9c8", "my shiny playlist", "test", false)
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "get" do
|
90
|
+
|
91
|
+
it "should get all playlists created by the user" do
|
92
|
+
@jotify.should_receive(:playlists).and_return( [@playlist] )
|
93
|
+
get '/playlists'
|
94
|
+
last_response.should be_ok
|
95
|
+
json_response.should == {"status"=>"OK",
|
96
|
+
"result"=> {
|
97
|
+
"playlists" => [
|
98
|
+
"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
99
|
+
"url"=>"http://open.spotify.com/user/test/playlist/2mnbxTkghYtlHMdX3jdP9C",
|
100
|
+
"name"=>"my shiny playlist",
|
101
|
+
"tracks"=>[], "author"=>"test", "revision"=>-1, "collaborative"=>false
|
102
|
+
]
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
it "should retrieve a playlist by id" do
|
109
|
+
@jotify.should_receive(:playlist).with("4d921ebcdd8c80f32ce1ed5acafbb9c8").and_return(@playlist)
|
110
|
+
get '/playlists/4d921ebcdd8c80f32ce1ed5acafbb9c8'
|
111
|
+
last_response.should be_ok
|
112
|
+
json_response.should == {"status"=>"OK",
|
113
|
+
"result"=>{
|
114
|
+
"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
115
|
+
"url"=>"http://open.spotify.com/user/test/playlist/2mnbxTkghYtlHMdX3jdP9C",
|
116
|
+
"name"=>"my shiny playlist",
|
117
|
+
"tracks"=>[], "author"=>"test", "revision"=>-1, "collaborative"=>false}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should except a base62 string as id" do
|
122
|
+
@jotify.should_receive(:playlist).with("2mnbxTkghYtlHMdX3jdP9C").and_return(@playlist)
|
123
|
+
get '/playlists/2mnbxTkghYtlHMdX3jdP9C'
|
124
|
+
last_response.should be_ok
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should return 404 for non-existing playlist" do
|
128
|
+
@jotify.should_receive(:playlist).with("unknown").and_return(nil)
|
129
|
+
get '/playlists/unknown'
|
130
|
+
last_response.status.should == 404
|
131
|
+
json_response.should == {"status"=>"ERROR", "message"=>"playlist not found"}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
describe "create/update" do
|
137
|
+
it "should create a playlist when posting to /playlists" do
|
138
|
+
@jotify.should_receive(:create_playlist).with('my shiny playlist', true).and_return(@playlist)
|
139
|
+
post '/playlists', :name => 'my shiny playlist', :collaborative => 'true'
|
140
|
+
last_response.status.should == 201
|
141
|
+
last_response.headers['Location'].should == 'http://open.spotify.com/user/test/playlist/2mnbxTkghYtlHMdX3jdP9C'
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should update playlist when putting to /playlists/id" do
|
145
|
+
@jotify.should_receive(:playlist).with("foo").and_return(@playlist)
|
146
|
+
@jotify.should_receive(:add_tracks_to_playlist).with(@playlist, ['1','2']).and_return(true)
|
147
|
+
put '/playlists/foo', { 'tracks' => [ {'id'=>'1' }, { 'id'=>'2' } ] }.to_json
|
148
|
+
last_response.should be_ok
|
149
|
+
json_response.should == {'status'=>'OK', 'message'=>'successfully added 2 tracks'}
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should return 404 if playlist to update cannot be found" do
|
153
|
+
@jotify.should_receive(:playlist).with("foo").and_return(nil)
|
154
|
+
put '/playlists/foo', { 'tracks' => [ {'id'=>'1' }, { 'id'=>'2' } ] }.to_json
|
155
|
+
last_response.status.should == 404
|
156
|
+
json_response.should == {"status"=>"ERROR", "message"=>"playlist not found"}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return 500 if playlist could not be updated" do
|
160
|
+
@jotify.should_receive(:playlist).with("foo").and_return(@playlist)
|
161
|
+
@jotify.should_receive(:add_tracks_to_playlist).with(@playlist, ['1','2']).and_return(false)
|
162
|
+
put '/playlists/foo', { 'tracks' => [ {'id'=>'1' }, { 'id'=>'2' } ] }.to_json
|
163
|
+
last_response.status.should == 500
|
164
|
+
json_response.should == {"status"=>"ERROR", "message"=>"could not add to playlist"}
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should return 403 if invalid data is supplied" do
|
168
|
+
@jotify.should_receive(:playlist).with("foo").and_return(@playlist)
|
169
|
+
lambda { put '/playlists/foo', { 'foo' => 'bar' }.to_json }.should raise_error(ArgumentError)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "shouldn't do anything if no track ids supplied" do
|
173
|
+
@jotify.should_receive(:playlist).with("foo").and_return(@playlist)
|
174
|
+
put '/playlists/foo', { 'tracks' => [] }.to_json
|
175
|
+
last_response.should be_ok
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env jruby -S spec
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
require 'jotify'
|
4
|
+
|
5
|
+
# WARNING: these specs need a valid spotify (premium) account, otherwise they will fail
|
6
|
+
describe "Integration" do
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
@jotify = Jotify.new
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) do
|
13
|
+
@jotify.close rescue nil
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "searching" do
|
17
|
+
it "should return a result for a search" do
|
18
|
+
result = @jotify.search("artist:Air")
|
19
|
+
result.should be_a(Jotify::Media::Result)
|
20
|
+
|
21
|
+
result.artists.should_not be_empty
|
22
|
+
most_popular = result.artists.to_a.sort(&Jotify::ByPopularity).first
|
23
|
+
most_popular.name.should == 'Air'
|
24
|
+
|
25
|
+
result = @jotify.search("album:Moon Safari")
|
26
|
+
result.albums.should_not be_empty
|
27
|
+
most_popular = result.albums.to_a.sort(&Jotify::ByPopularity).first
|
28
|
+
most_popular.name.should == 'Moon Safari'
|
29
|
+
most_popular.artist.name.should == 'Air'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/jotify_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env jruby -S spec
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
require 'jotify'
|
4
|
+
|
5
|
+
describe Jotify do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@jotify_impl = mock('JotifyImpl')
|
9
|
+
@jotify_impl.stub!(:login)
|
10
|
+
@jotify = Jotify.new(@jotify_impl)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should resolve ids" do
|
14
|
+
{ "spotify:user:flumix:playlist:2mnbxTkghYtlHMdX3jdP9C" => "4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
15
|
+
"http://open.spotify.com/user/flumix/playlist/2mnbxTkghYtlHMdX3jdP9C" => "4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
16
|
+
"2mnbxTkghYtlHMdX3jdP9C" => "4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
17
|
+
"4d921ebcdd8c80f32ce1ed5acafbb9c8" => "4d921ebcdd8c80f32ce1ed5acafbb9c8"
|
18
|
+
}.each { |id, expected| Jotify.resolve_id(id).should == expected }
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should add tracks to playlist" do
|
22
|
+
@playlist = Jotify::Media::Playlist.new
|
23
|
+
@jotify_impl.should_receive(:playlistAddTracks) do |playlist, tracks, pos|
|
24
|
+
playlist.should be_a(Jotify::Media::Playlist)
|
25
|
+
#playlist.should == @playlist
|
26
|
+
pos.should == 0
|
27
|
+
tracks.should be_an(Java::JavaUtil::List)
|
28
|
+
tracks.size.should == 1
|
29
|
+
end
|
30
|
+
@jotify.add_tracks_to_playlist(@playlist, ['4d921ebcdd8c80f32ce1ed5acafbb9c8'])
|
31
|
+
end
|
32
|
+
end
|
data/spec/media_spec.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env jruby -S spec
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
require 'jotify'
|
4
|
+
|
5
|
+
describe Jotify::Media do
|
6
|
+
|
7
|
+
describe "an artist" do
|
8
|
+
before do
|
9
|
+
@artist = Jotify::Media::Artist.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should implement to_h" do
|
13
|
+
@artist.respond_to?(:to_h).should be_true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "a track" do
|
18
|
+
before do
|
19
|
+
@track = Jotify::Media::Track.new
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should implement to_h" do
|
23
|
+
@track.respond_to?(:to_h).should be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "an album" do
|
28
|
+
before do
|
29
|
+
@album = Jotify::Media::Album.new
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should implement to_h" do
|
33
|
+
@album.respond_to?(:to_h).should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should implement enumerable" do
|
37
|
+
@album.class.included_modules.should include(Enumerable)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "a result" do
|
42
|
+
before do
|
43
|
+
@result = Jotify::Media::Result.new
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should implement to_h" do
|
47
|
+
@result.respond_to?(:to_h).should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should implement enumerable" do
|
51
|
+
@result.class.included_modules.should include(Enumerable)
|
52
|
+
@result.respond_to?(:each).should be_true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "playlists" do
|
57
|
+
it "should implement enumerable" do
|
58
|
+
p = Jotify::Media::Playlist.new
|
59
|
+
p.class.included_modules.should include(Enumerable)
|
60
|
+
p.respond_to?(:each).should be_true
|
61
|
+
10.times { p.tracks.add(Jotify::Media::Track.new) }
|
62
|
+
p.to_a.size.should == 10
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "playlist container" do
|
67
|
+
before do
|
68
|
+
@container = Jotify::Media::PlaylistContainer.new
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should implement enumerable" do
|
72
|
+
@container.respond_to?(:each).should be_true
|
73
|
+
@container.class.included_modules.should include(Enumerable)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spotify-api.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{spotify-api}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Jan Berkel"]
|
9
|
+
s.date = %q{2009-08-04}
|
10
|
+
s.default_executable = %q{spotify-api-server}
|
11
|
+
s.description = %q{an api for spotify, based on jotify}
|
12
|
+
s.email = %q{jan.berkel@gmail.com}
|
13
|
+
s.executables = ["spotify-api-server"]
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"LICENSE",
|
21
|
+
"README.md",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION.yml",
|
24
|
+
"bin/spotify-api-server",
|
25
|
+
"examples/create_playlist.rb",
|
26
|
+
"examples/list_playlists.rb",
|
27
|
+
"examples/search.rb",
|
28
|
+
"lib/jars/jotify.jar",
|
29
|
+
"lib/jotify.rb",
|
30
|
+
"lib/jotify/api.rb",
|
31
|
+
"lib/jotify/media.rb",
|
32
|
+
"spec/api_spec.rb",
|
33
|
+
"spec/integration_spec.rb",
|
34
|
+
"spec/jotify_spec.rb",
|
35
|
+
"spec/media_spec.rb",
|
36
|
+
"spec/spec_helper.rb",
|
37
|
+
"spotify-api.gemspec"
|
38
|
+
]
|
39
|
+
s.homepage = %q{http://github.com/jberkel/spotify-api}
|
40
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.5}
|
43
|
+
s.summary = %q{an api for spotify, based on jotify}
|
44
|
+
s.test_files = [
|
45
|
+
"spec/api_spec.rb",
|
46
|
+
"spec/integration_spec.rb",
|
47
|
+
"spec/jotify_spec.rb",
|
48
|
+
"spec/media_spec.rb",
|
49
|
+
"spec/spec_helper.rb",
|
50
|
+
"examples/create_playlist.rb",
|
51
|
+
"examples/list_playlists.rb",
|
52
|
+
"examples/search.rb"
|
53
|
+
]
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
57
|
+
s.specification_version = 3
|
58
|
+
|
59
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
60
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
61
|
+
s.add_runtime_dependency(%q<rack-test>, [">= 0"])
|
62
|
+
s.add_runtime_dependency(%q<sinatra>, [">= 0"])
|
63
|
+
s.add_runtime_dependency(%q<json-jruby>, [">= 0"])
|
64
|
+
else
|
65
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
66
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
67
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
68
|
+
s.add_dependency(%q<json-jruby>, [">= 0"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
72
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
73
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
74
|
+
s.add_dependency(%q<json-jruby>, [">= 0"])
|
75
|
+
end
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jberkel-spotify-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jan Berkel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-04 00:00:00 -07:00
|
13
|
+
default_executable: spotify-api-server
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack-test
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sinatra
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: json-jruby
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
description: an api for spotify, based on jotify
|
56
|
+
email: jan.berkel@gmail.com
|
57
|
+
executables:
|
58
|
+
- spotify-api-server
|
59
|
+
extensions: []
|
60
|
+
|
61
|
+
extra_rdoc_files:
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- LICENSE
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- VERSION.yml
|
70
|
+
- bin/spotify-api-server
|
71
|
+
- examples/create_playlist.rb
|
72
|
+
- examples/list_playlists.rb
|
73
|
+
- examples/search.rb
|
74
|
+
- lib/jars/jotify.jar
|
75
|
+
- lib/jotify.rb
|
76
|
+
- lib/jotify/api.rb
|
77
|
+
- lib/jotify/media.rb
|
78
|
+
- spec/api_spec.rb
|
79
|
+
- spec/integration_spec.rb
|
80
|
+
- spec/jotify_spec.rb
|
81
|
+
- spec/media_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spotify-api.gemspec
|
84
|
+
has_rdoc: false
|
85
|
+
homepage: http://github.com/jberkel/spotify-api
|
86
|
+
licenses:
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options:
|
89
|
+
- --charset=UTF-8
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "0"
|
103
|
+
version:
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.3.5
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: an api for spotify, based on jotify
|
111
|
+
test_files:
|
112
|
+
- spec/api_spec.rb
|
113
|
+
- spec/integration_spec.rb
|
114
|
+
- spec/jotify_spec.rb
|
115
|
+
- spec/media_spec.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
- examples/create_playlist.rb
|
118
|
+
- examples/list_playlists.rb
|
119
|
+
- examples/search.rb
|