spotify-api 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +1 -1
- data/README.md +8 -4
- data/Rakefile +1 -0
- data/VERSION.yml +1 -1
- data/examples/lastfm2spotify_loved_tracks +31 -0
- data/examples/lastfm2spotify_metrochart +35 -0
- data/examples/lastfm_metro_playlists +87 -0
- data/lib/clients/lastfm.rb +81 -0
- data/{examples → lib/clients}/spotify.rb +15 -5
- data/lib/jars/jotify.jar +0 -0
- data/lib/jotify/api.rb +42 -13
- data/spec/jotify/api_spec.rb +3 -2
- data/spotify-api.gemspec +14 -9
- metadata +17 -8
- data/examples/lastfm.rb +0 -43
- data/examples/lastfm2spotify.rb +0 -20
data/CHANGELOG
CHANGED
data/LICENSE
CHANGED
@@ -15,7 +15,7 @@ modification, are permitted provided that the following conditions are met:
|
|
15
15
|
THIS SOFTWARE IS PROVIDED BY Jan Berkel 'AS IS'' AND ANY
|
16
16
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
17
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
-
DISCLAIMED. IN NO EVENT SHALL
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL Jan Berkel BE LIABLE FOR ANY
|
19
19
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
20
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
21
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
data/README.md
CHANGED
@@ -20,8 +20,7 @@ At the moment the following features are implemented:
|
|
20
20
|
|
21
21
|
Prerequisites: *Java 6+*, JRuby 1.3.x.
|
22
22
|
|
23
|
-
$ jruby -S gem
|
24
|
-
$ jruby -S gem install jberkel-spotify-api
|
23
|
+
$ jruby -S gem install spotify-api --source http://gemcutter.org
|
25
24
|
$ jruby -S spotify-api-server --account login:password
|
26
25
|
== Sinatra/0.9.4 has taken the stage on 3000 for development with backup from WEBrick
|
27
26
|
[2009-08-04 01:21:03] INFO WEBrick 1.3.1
|
@@ -38,14 +37,19 @@ Prerequisites: *Java 6+*, JRuby 1.3.x.
|
|
38
37
|
"url": "http:\/\/open.spotify.com\/user\/jberkel\/playlist\/5EXLGE7HPVPjvlxPmIfrDe",
|
39
38
|
"revision": 2,
|
40
39
|
"id": "b9fe3dcf88945d146ef18117faa61ab4",
|
40
|
+
"size": 1,
|
41
41
|
"collaborative": false
|
42
42
|
}
|
43
43
|
]
|
44
44
|
},
|
45
45
|
"status": "OK"
|
46
46
|
}
|
47
|
-
|
48
|
-
|
47
|
+
|
48
|
+
## Examples
|
49
|
+
|
50
|
+
See examples directory for usage. The demo application ([lastfm2spotify_loved_tracks](/jberkel/spotify-api/blob/master/examples/lastfm2spotify_loved_tracks)) will
|
51
|
+
grab all recently loved tracks by your last.fm friends to create a new
|
52
|
+
spotify playlist. A nice way to listen to some good new music (if you share a similar music taste with most of your friends :)).
|
49
53
|
|
50
54
|
## Credits
|
51
55
|
|
data/Rakefile
CHANGED
@@ -15,6 +15,7 @@ begin
|
|
15
15
|
gem.add_dependency "rack-test"
|
16
16
|
gem.add_dependency "sinatra"
|
17
17
|
gem.add_dependency "json-jruby"
|
18
|
+
gem.add_dependency "httparty"
|
18
19
|
end
|
19
20
|
rescue LoadError
|
20
21
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
data/VERSION.yml
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'spotify-api'
|
7
|
+
|
8
|
+
require 'clients/lastfm'
|
9
|
+
require 'clients/spotify'
|
10
|
+
|
11
|
+
# a demo app which grabs tracks from last.fm and creates a spotify
|
12
|
+
# playlist based on friends loved tracks
|
13
|
+
if __FILE__ == $0
|
14
|
+
username = ARGV.shift or raise "#{$0} <username>"
|
15
|
+
|
16
|
+
puts "fetching last.fm tracks (friends/recently_loved)"
|
17
|
+
limit = 10 # 10 tracks per friend
|
18
|
+
tracks = LastFM.friends_loved_tracks(username, limit).values.flatten
|
19
|
+
|
20
|
+
puts "resolving spotify ids"
|
21
|
+
spotify_tracks = begin
|
22
|
+
Spotify.resolve(tracks.map {|t| [t["title"], t["artist"]] })
|
23
|
+
rescue Errno::ECONNREFUSED
|
24
|
+
fail "Could not connect to spotify-api server. Make sure it it running."
|
25
|
+
end
|
26
|
+
|
27
|
+
if spotify_tracks.size > 0
|
28
|
+
puts "creating playlist with #{spotify_tracks.size} tracks"
|
29
|
+
puts Spotify.create_playlist("loved by friends of #{username}", spotify_tracks.map { |t| t['id'] })
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'spotify-api'
|
7
|
+
|
8
|
+
require 'clients/lastfm'
|
9
|
+
require 'clients/spotify'
|
10
|
+
|
11
|
+
# a demo app which grabs tracks from last.fm and creates a spotify
|
12
|
+
# playlist based on tracks popular in a city
|
13
|
+
|
14
|
+
# to batch create playlists, use lastfm_metro_playlists.rb
|
15
|
+
if __FILE__ == $0
|
16
|
+
city = ARGV.shift or raise "#{$0} <city,country>"
|
17
|
+
|
18
|
+
metro,country = *city.split(',')
|
19
|
+
|
20
|
+
puts "fetching last.fm tracks for #{metro}, #{country}"
|
21
|
+
unique = true
|
22
|
+
tracks = LastFM.metro_track_chart(metro,country, unique)
|
23
|
+
|
24
|
+
puts "resolving spotify ids"
|
25
|
+
spotify_tracks = begin
|
26
|
+
Spotify.resolve(tracks.map { |t| [t["name"], t["artist"]["name"]] })
|
27
|
+
rescue Errno::ECONNREFUSED
|
28
|
+
fail "Could not connect to spotify-api server. Make sure it it running."
|
29
|
+
end
|
30
|
+
|
31
|
+
if spotify_tracks.size > 0
|
32
|
+
puts "creating playlist with #{spotify_tracks.size} tracks"
|
33
|
+
puts Spotify.create_playlist("chart for #{metro}, #{country}", spotify_tracks.map { |t| t['id'] })
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'spotify-api'
|
7
|
+
|
8
|
+
require 'clients/lastfm'
|
9
|
+
require 'clients/spotify'
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
metro_areas = [
|
14
|
+
["Berlin", "Germany"],
|
15
|
+
["London", "United Kingdom"],
|
16
|
+
["Barcelona", "Spain"],
|
17
|
+
["Paris", "France"],
|
18
|
+
["Rome", "Italy"],
|
19
|
+
["Los Angeles", "United States"],
|
20
|
+
["Istanbul", "Turkey"],
|
21
|
+
["Vienna", "Austria"],
|
22
|
+
["Zurich", "Switzerland"],
|
23
|
+
["New York", "United States"],
|
24
|
+
["Oslo", "Norway"],
|
25
|
+
["Stockholm", "Sweden"],
|
26
|
+
["Melbourne", "Australia"],
|
27
|
+
["Montreal", "Canada"],
|
28
|
+
["Auckland", "New Zealand"]
|
29
|
+
]
|
30
|
+
|
31
|
+
def fetch_existing_playlists
|
32
|
+
Spotify.playlists['playlists'].inject({}) do |h, p|
|
33
|
+
if p['name'] =~ /chart for ([\w\s]+), ([\w\s]+) (\([\d-]+\))\Z/
|
34
|
+
city, metro = $1, $2
|
35
|
+
h[[city, metro]] = p
|
36
|
+
end
|
37
|
+
h
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_playlists(areas, update=false, unique=true)
|
42
|
+
puts "fetching existing playlists"
|
43
|
+
existing_playlists = fetch_existing_playlists
|
44
|
+
time_period = LastFM.metro_weekly_chartlist.first
|
45
|
+
|
46
|
+
areas.each do |(metro, country)|
|
47
|
+
if existing_playlists[[metro, country]] && !update
|
48
|
+
puts "#{metro}, #{country} already exists, skipping"
|
49
|
+
next
|
50
|
+
end
|
51
|
+
|
52
|
+
puts "fetching last.fm tracks for #{metro}, #{country}"
|
53
|
+
tracks = LastFM.metro_track_chart(metro, country, unique, time_period['from'], time_period['to'])
|
54
|
+
puts "resolving spotify ids"
|
55
|
+
spotify_tracks = Spotify.resolve(tracks.map { |t| [t["name"], t["artist"]["name"]] })
|
56
|
+
|
57
|
+
next if spotify_tracks.empty?
|
58
|
+
|
59
|
+
if playlist = existing_playlists[[metro, country]]
|
60
|
+
puts "update existing playlist #{playlist['id']} with #{spotify_tracks.size} tracks"
|
61
|
+
Spotify.update_playlist(playlist['id'], nil, spotify_tracks.map { |t| t['id'] })
|
62
|
+
else
|
63
|
+
tstamp = Time.new.strftime("%m-%d-%Y")
|
64
|
+
puts "creating new playlist with #{spotify_tracks.size} tracks"
|
65
|
+
puts Spotify.create_playlist("last.fm chart for #{metro}, #{country} (#{tstamp})", spotify_tracks.map { |t| t['id'] })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def dump_playlists
|
71
|
+
puts fetch_existing_playlists.values.to_json
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
if __FILE__ == $0
|
76
|
+
if ARGV.empty?
|
77
|
+
STDERR.puts "#{$0} [create|update|dump]"
|
78
|
+
exit(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
case cmd = ARGV.shift
|
82
|
+
when 'update': generate_playlists(metro_areas, true)
|
83
|
+
when 'create': generate_playlists(metro_areas)
|
84
|
+
when 'dump': dump_playlists
|
85
|
+
else raise ArgumentError, "invalid command #{cmd}"
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'httparty'
|
5
|
+
|
6
|
+
#Last.fm API client, implemented
|
7
|
+
#using httparty (http://github.com/jnunemaker/httparty/)
|
8
|
+
#
|
9
|
+
#NOTE: you need to set the LAST_FM_API_KEY environment variable to your API key
|
10
|
+
#before you can use this class.
|
11
|
+
class LastFM
|
12
|
+
include HTTParty
|
13
|
+
|
14
|
+
base_uri 'ws.audioscrobbler.com'
|
15
|
+
default_params :api_key => (ENV['LAST_FM_API_KEY'] or raise "You need to set the LAST_FM_API_KEY environment variable")
|
16
|
+
|
17
|
+
class <<self
|
18
|
+
def loved_tracks(user_id, limit=5)
|
19
|
+
if tracks = query('user.getLovedTracks', :user=>user_id, :limit=>limit)['lovedtracks']['track']
|
20
|
+
tracks.map do |r|
|
21
|
+
{ 'artist' => r['artist']['name'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
22
|
+
end
|
23
|
+
else
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def recent_tracks(user_id)
|
29
|
+
query('user.getRecentTracks', :user=>user_id, :limit=>100)['recenttracks']['track'].map do |r|
|
30
|
+
{ 'artist' => r['artist'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def top_tracks(user_id, period='overall')
|
35
|
+
unless ['overall', '7day', '3month', '6month', '12month'].include?(period)
|
36
|
+
raise ArgumentError, "invalid period"
|
37
|
+
end
|
38
|
+
|
39
|
+
query('user.getTopTracks', :period=>period, :user=>user_id)['toptracks']['track'].map do |r|
|
40
|
+
{ 'artist' => r['artist']['name'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def friends(user_id)
|
45
|
+
query('user.getfriends', :user=>user_id, :recenttracks=>false)['friends']['user']
|
46
|
+
end
|
47
|
+
|
48
|
+
def neighbours(user_id, max=10)
|
49
|
+
query('user.getneighbours', :user=>user_id, :limit=>10)#['friends']['user']
|
50
|
+
end
|
51
|
+
|
52
|
+
# retrieve tracks recently loved by friends
|
53
|
+
def friends_loved_tracks(user_id, limit=5)
|
54
|
+
friends(user_id).inject({}) do |h, u|
|
55
|
+
h[u['name']] = loved_tracks(u['name'], limit)
|
56
|
+
sleep 0.5
|
57
|
+
h
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def metro_weekly_chartlist
|
62
|
+
query("geo.getMetroWeeklyChartlist")['weeklychartlist']['chart'].reverse
|
63
|
+
end
|
64
|
+
|
65
|
+
def metro_track_chart(city, country, unique=false, from=nil, to=nil)
|
66
|
+
args = { :country=>country, :metro=>city }
|
67
|
+
args.merge!(:start=>from, :end=>to) if from && to
|
68
|
+
|
69
|
+
res = query("geo.getMetro#{unique ? 'Unique' : ''}TrackChart", args)
|
70
|
+
return [] unless res['toptracks'] && res['toptracks']['track']
|
71
|
+
res['toptracks']['track']
|
72
|
+
end
|
73
|
+
|
74
|
+
def query(method, args={})
|
75
|
+
result = get("/2.0/", :query => { :method => method }.merge(args))
|
76
|
+
raise result['lfm']['error'] if result['lfm'] && result['lfm']['status'] == 'failed'
|
77
|
+
result['lfm']
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -5,8 +5,7 @@ require 'httparty'
|
|
5
5
|
require 'json'
|
6
6
|
require 'pp'
|
7
7
|
|
8
|
-
#A
|
9
|
-
#using httparty (http://github.com/jnunemaker/httparty/)
|
8
|
+
#A spotify-api API client.
|
10
9
|
class Spotify
|
11
10
|
include HTTParty
|
12
11
|
|
@@ -42,12 +41,12 @@ class Spotify
|
|
42
41
|
get_or_bail("/playlists")
|
43
42
|
end
|
44
43
|
|
45
|
-
def self.update_playlist(
|
44
|
+
def self.update_playlist(playlist_id, name=nil, track_ids=[])
|
46
45
|
data = {}
|
47
|
-
data["tracks"] = track_ids.map { |
|
46
|
+
data["tracks"] = track_ids.map { |t_id| { 'id' => t_id } } unless track_ids.empty?
|
48
47
|
data["name"] = name if name
|
49
48
|
|
50
|
-
resp = put("/playlists/#{
|
49
|
+
resp = put("/playlists/#{playlist_id}", :body => data.to_json)
|
51
50
|
raise resp.inspect if resp['status'] != 'OK'
|
52
51
|
end
|
53
52
|
|
@@ -64,6 +63,17 @@ class Spotify
|
|
64
63
|
raise resp.inspect
|
65
64
|
end
|
66
65
|
end
|
66
|
+
|
67
|
+
def self.resolve(tracks)
|
68
|
+
tracks.map do |(title, artist)|
|
69
|
+
begin
|
70
|
+
Spotify.tracks(title, artist).first
|
71
|
+
rescue RuntimeError
|
72
|
+
nil
|
73
|
+
#raise
|
74
|
+
end
|
75
|
+
end.flatten.compact
|
76
|
+
end
|
67
77
|
end
|
68
78
|
|
69
79
|
if __FILE__ == $0
|
data/lib/jars/jotify.jar
CHANGED
Binary file
|
data/lib/jotify/api.rb
CHANGED
@@ -16,6 +16,11 @@ class Sinatra::Application
|
|
16
16
|
def query(what, p=what)
|
17
17
|
params[p] ? "#{what}:" + params[p] : ''
|
18
18
|
end
|
19
|
+
|
20
|
+
def error(code, message)
|
21
|
+
[code, { 'status' => 'ERROR', 'message' => message }.to_json]
|
22
|
+
end
|
23
|
+
|
19
24
|
end
|
20
25
|
|
21
26
|
Sinatra::Application.error ArgumentError do
|
@@ -65,20 +70,28 @@ Sinatra::Application.get('/playlists') do
|
|
65
70
|
'status'=>'OK',
|
66
71
|
'result'=> { 'playlists' => jotify.playlists.map do |p|
|
67
72
|
p.to_h
|
68
|
-
end.each { |h|
|
73
|
+
end.each { |h|
|
74
|
+
h['size'] = h.delete(:tracks).size
|
75
|
+
}
|
69
76
|
}
|
70
77
|
}.to_json
|
71
78
|
end
|
72
79
|
|
73
80
|
Sinatra::Application.get('/playlists/:id') do
|
74
81
|
content_type :json
|
75
|
-
|
82
|
+
playlist = begin
|
83
|
+
jotify.playlist(params[:id])
|
84
|
+
rescue Exception => e
|
85
|
+
return error(500, "error getting playlist: #{e.message}")
|
86
|
+
end
|
87
|
+
|
88
|
+
if playlist
|
76
89
|
{
|
77
90
|
'status'=>'OK',
|
78
91
|
'result'=>playlist.to_h
|
79
92
|
}.to_json
|
80
93
|
else
|
81
|
-
|
94
|
+
error(404, 'playlist not found')
|
82
95
|
end
|
83
96
|
end
|
84
97
|
|
@@ -86,25 +99,36 @@ Sinatra::Application.post('/playlists') do
|
|
86
99
|
content_type :json
|
87
100
|
body = request.body.read
|
88
101
|
data = JSON.parse(body)
|
89
|
-
|
102
|
+
|
103
|
+
playlist = begin
|
104
|
+
jotify.create_playlist(data['name'], !!data['collaborative'])
|
105
|
+
rescue Exception => e
|
106
|
+
return error(500, "error creating playlist: #{e.message}")
|
107
|
+
end
|
108
|
+
|
90
109
|
if playlist
|
91
110
|
if data['tracks']
|
92
111
|
ids = data['tracks'].map { |t| t['id'] }
|
93
112
|
unless jotify.set_tracks_on_playlist(playlist, ids)
|
94
|
-
return 500, '
|
113
|
+
return error(500, 'playlist created but tracks could not be added')
|
95
114
|
end
|
96
115
|
end
|
97
116
|
redirect playlist.link, 201 # created
|
98
117
|
else
|
99
|
-
|
118
|
+
error(500, 'playlist could not be created')
|
100
119
|
end
|
101
120
|
end
|
102
121
|
|
103
122
|
Sinatra::Application.put('/playlists/:id') do
|
104
123
|
content_type :json
|
105
|
-
|
124
|
+
|
125
|
+
playlist = begin
|
126
|
+
jotify.playlist(params[:id])
|
127
|
+
rescue Exception => e
|
128
|
+
return error(500, "error getting playlist: #{e.message}")
|
129
|
+
end
|
106
130
|
|
107
|
-
return 404,
|
131
|
+
return error(404, 'playlist not found') unless playlist
|
108
132
|
body = request.body.read
|
109
133
|
data = JSON.parse(body)
|
110
134
|
|
@@ -112,21 +136,26 @@ Sinatra::Application.put('/playlists/:id') do
|
|
112
136
|
|
113
137
|
if data.has_key?('name') && data['name'] != playlist.name
|
114
138
|
unless jotify.rename_playlist(playlist, data['name'])
|
115
|
-
return 500,
|
139
|
+
return error(500, 'could rename playlist')
|
116
140
|
end
|
117
141
|
end
|
118
142
|
|
119
143
|
if data.has_key?('collaborative') && data['collaborative'] != playlist.collaborative?
|
120
144
|
unless jotify.set_collaborative_flag(playlist, data['collaborative'])
|
121
|
-
return 500,
|
145
|
+
return error(500, 'could not change collaborative flag')
|
122
146
|
end
|
123
147
|
end
|
124
148
|
|
125
149
|
if data['tracks'].is_a?(Array)
|
126
150
|
ids = data['tracks'].map { |t| t['id'] }
|
127
|
-
|
128
|
-
|
129
|
-
|
151
|
+
begin
|
152
|
+
unless jotify.set_tracks_on_playlist(playlist, ids)
|
153
|
+
return error(500, 'could not update tracks')
|
154
|
+
end
|
155
|
+
rescue Exception => e
|
156
|
+
return error(500, e.to_s)
|
157
|
+
end
|
158
|
+
|
130
159
|
end
|
131
160
|
return 200, { 'status' => 'OK', 'message' => "update successful" }.to_json
|
132
161
|
end
|
data/spec/jotify/api_spec.rb
CHANGED
@@ -99,6 +99,7 @@ describe 'Api' do
|
|
99
99
|
"id"=>"4d921ebcdd8c80f32ce1ed5acafbb9c8",
|
100
100
|
"url"=>"http://open.spotify.com/user/test/playlist/2mnbxTkghYtlHMdX3jdP9C",
|
101
101
|
"name"=>"my shiny playlist",
|
102
|
+
"size"=>0,
|
102
103
|
"author"=>"test", "revision"=>-1, "collaborative"=>false
|
103
104
|
]
|
104
105
|
}
|
@@ -204,7 +205,7 @@ describe 'Api' do
|
|
204
205
|
@jotify.should_receive(:set_tracks_on_playlist).with(@playlist, ['1','2']).and_return(false)
|
205
206
|
put '/playlists/foo', { 'tracks' => [ {'id'=>'1' }, { 'id'=>'2' } ] }.to_json
|
206
207
|
last_response.status.should == 500
|
207
|
-
json_response.should == {"status"=>"ERROR", "message"=>"could update tracks"}
|
208
|
+
json_response.should == {"status"=>"ERROR", "message"=>"could not update tracks"}
|
208
209
|
end
|
209
210
|
|
210
211
|
# it "should return 403 if invalid data is supplied" do
|
@@ -219,4 +220,4 @@ describe 'Api' do
|
|
219
220
|
end
|
220
221
|
end
|
221
222
|
end
|
222
|
-
end
|
223
|
+
end
|
data/spotify-api.gemspec
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{spotify-api}
|
5
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.6"
|
6
9
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
11
|
s.authors = ["Jan Berkel"]
|
9
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-26}
|
10
13
|
s.default_executable = %q{spotify-api-server}
|
11
14
|
s.description = %q{an api for spotify, based on jotify}
|
12
15
|
s.email = %q{jan.berkel@gmail.com}
|
@@ -23,9 +26,11 @@ Gem::Specification.new do |s|
|
|
23
26
|
"Rakefile",
|
24
27
|
"VERSION.yml",
|
25
28
|
"bin/spotify-api-server",
|
26
|
-
"examples/
|
27
|
-
"examples/
|
28
|
-
"examples/
|
29
|
+
"examples/lastfm2spotify_loved_tracks",
|
30
|
+
"examples/lastfm2spotify_metrochart",
|
31
|
+
"examples/lastfm_metro_playlists",
|
32
|
+
"lib/clients/lastfm.rb",
|
33
|
+
"lib/clients/spotify.rb",
|
29
34
|
"lib/jars/jotify.jar",
|
30
35
|
"lib/jotify.rb",
|
31
36
|
"lib/jotify/api.rb",
|
@@ -47,10 +52,7 @@ Gem::Specification.new do |s|
|
|
47
52
|
"spec/jotify/api_spec.rb",
|
48
53
|
"spec/jotify/media_spec.rb",
|
49
54
|
"spec/jotify_spec.rb",
|
50
|
-
"spec/spec_helper.rb"
|
51
|
-
"examples/lastfm.rb",
|
52
|
-
"examples/lastfm2spotify.rb",
|
53
|
-
"examples/spotify.rb"
|
55
|
+
"spec/spec_helper.rb"
|
54
56
|
]
|
55
57
|
|
56
58
|
if s.respond_to? :specification_version then
|
@@ -62,16 +64,19 @@ Gem::Specification.new do |s|
|
|
62
64
|
s.add_runtime_dependency(%q<rack-test>, [">= 0"])
|
63
65
|
s.add_runtime_dependency(%q<sinatra>, [">= 0"])
|
64
66
|
s.add_runtime_dependency(%q<json-jruby>, [">= 0"])
|
67
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
65
68
|
else
|
66
69
|
s.add_dependency(%q<rack>, [">= 0"])
|
67
70
|
s.add_dependency(%q<rack-test>, [">= 0"])
|
68
71
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
69
72
|
s.add_dependency(%q<json-jruby>, [">= 0"])
|
73
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
70
74
|
end
|
71
75
|
else
|
72
76
|
s.add_dependency(%q<rack>, [">= 0"])
|
73
77
|
s.add_dependency(%q<rack-test>, [">= 0"])
|
74
78
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
75
79
|
s.add_dependency(%q<json-jruby>, [">= 0"])
|
80
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
76
81
|
end
|
77
82
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spotify-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Berkel
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-26 00:00:00 +01:00
|
13
13
|
default_executable: spotify-api-server
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -52,6 +52,16 @@ dependencies:
|
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: "0"
|
54
54
|
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: httparty
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
55
65
|
description: an api for spotify, based on jotify
|
56
66
|
email: jan.berkel@gmail.com
|
57
67
|
executables:
|
@@ -69,9 +79,11 @@ files:
|
|
69
79
|
- Rakefile
|
70
80
|
- VERSION.yml
|
71
81
|
- bin/spotify-api-server
|
72
|
-
- examples/
|
73
|
-
- examples/
|
74
|
-
- examples/
|
82
|
+
- examples/lastfm2spotify_loved_tracks
|
83
|
+
- examples/lastfm2spotify_metrochart
|
84
|
+
- examples/lastfm_metro_playlists
|
85
|
+
- lib/clients/lastfm.rb
|
86
|
+
- lib/clients/spotify.rb
|
75
87
|
- lib/jars/jotify.jar
|
76
88
|
- lib/jotify.rb
|
77
89
|
- lib/jotify/api.rb
|
@@ -116,6 +128,3 @@ test_files:
|
|
116
128
|
- spec/jotify/media_spec.rb
|
117
129
|
- spec/jotify_spec.rb
|
118
130
|
- spec/spec_helper.rb
|
119
|
-
- examples/lastfm.rb
|
120
|
-
- examples/lastfm2spotify.rb
|
121
|
-
- examples/spotify.rb
|
data/examples/lastfm.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'httparty'
|
5
|
-
|
6
|
-
#A demo Last.fm API client, implemented
|
7
|
-
#using httparty (http://github.com/jnunemaker/httparty/)
|
8
|
-
class Lastfm
|
9
|
-
include HTTParty
|
10
|
-
|
11
|
-
base_uri 'ws.audioscrobbler.com'
|
12
|
-
default_params :api_key => "PUT_API_KEY_HERE"
|
13
|
-
|
14
|
-
class <<self
|
15
|
-
def loved_tracks(user_id)
|
16
|
-
query('user.getLovedTracks', :user=>user_id, :limit=>10)['lovedtracks']['track'].map do |r|
|
17
|
-
{ 'artist' => r['artist']['name'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def recent_tracks(user_id)
|
22
|
-
query('user.getRecentTracks', :user=>user_id, :limit=>100)['recenttracks']['track'].map do |r|
|
23
|
-
{ 'artist' => r['artist'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def top_tracks(user_id, period='overall')
|
28
|
-
unless ['overall', '7day', '3month', '6month', '12month'].include?(period)
|
29
|
-
raise ArgumentError, "invalid period"
|
30
|
-
end
|
31
|
-
|
32
|
-
query('user.getTopTracks', :period=>period, :user=>user_id)['toptracks']['track'].map do |r|
|
33
|
-
{ 'artist' => r['artist']['name'], 'title'=>r['name'], 'mbid' => r['mbid'] }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def query(method, args={})
|
38
|
-
result = get("/2.0/", :query => { :method => method }.merge(args))
|
39
|
-
raise result['lfm']['error'] if result['lfm'] && result['lfm']['status'] == 'failed'
|
40
|
-
result['lfm']
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/examples/lastfm2spotify.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require File.join(File.dirname(__FILE__), *%w[lastfm])
|
4
|
-
require File.join(File.dirname(__FILE__), *%w[spotify])
|
5
|
-
|
6
|
-
# a demo app which grabs tracks from last.fm and creates a spotify
|
7
|
-
# playlist
|
8
|
-
if __FILE__ == $0
|
9
|
-
username = ARGV.shift or raise "#{$0} <username> [period=overall|7day|3month|6month|12month]"
|
10
|
-
period = ARGV.shift || '7day'
|
11
|
-
|
12
|
-
puts "fetching last.fm tracks (period=#{period})"
|
13
|
-
tracks = Lastfm.top_tracks(username, period).map do |track|
|
14
|
-
Spotify.tracks(track["title"], track["artist"]).first
|
15
|
-
end.flatten.compact
|
16
|
-
|
17
|
-
#puts "found tracks: #{tracks.inspect}"
|
18
|
-
puts "creating playlist with #{tracks.size} tracks"
|
19
|
-
puts Spotify.create_playlist(username, tracks.map { |t| t['id'] })
|
20
|
-
end
|