soundcloud 0.1.3b
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/README.md +110 -0
- data/lib/soundcloud.rb +123 -0
- data/lib/soundcloud/array_response_wrapper.rb +8 -0
- data/lib/soundcloud/hash_response_wrapper.rb +7 -0
- data/lib/soundcloud/version.rb +3 -0
- metadata +146 -0
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Soundcloud API Wrapper
|
2
|
+
## Description
|
3
|
+
This is a thin wrapper around the Soundcloud API based of httparty.
|
4
|
+
It is providing simple methods to handle authorization and to execute HTTP calls.
|
5
|
+
|
6
|
+
## Requirements
|
7
|
+
* httmultiparty
|
8
|
+
* httparty
|
9
|
+
* crack
|
10
|
+
* multipart-upload
|
11
|
+
* hashie
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
gem install soundcloud
|
15
|
+
|
16
|
+
## Examples
|
17
|
+
#### print links of the 10 hottest tracks
|
18
|
+
# register a client with YOUR_CLIENT_ID as client_id_
|
19
|
+
client = Soundcloud.new(:client_id => 'YOUR_CLIENT_ID')
|
20
|
+
# get 10 hottest tracks
|
21
|
+
tracks = client.get('/tracks', :limit => 10, :order => 'hotness')
|
22
|
+
# print each link
|
23
|
+
tracks.each do |track|
|
24
|
+
puts track.permalink_url
|
25
|
+
end
|
26
|
+
|
27
|
+
#### Do the OAuth2 user credentials flow and print the username of the authenticated user
|
28
|
+
# register a new client, which will exchange the username, password for an access_token
|
29
|
+
client = Soundcloud.new({
|
30
|
+
:client_id => 'YOUR_CLIENT_ID',
|
31
|
+
:client_secret => 'YOUR_CLIENT_SECRET',
|
32
|
+
:username => 'some@email.com',
|
33
|
+
:password => 'userpass'
|
34
|
+
})
|
35
|
+
|
36
|
+
# print logged in username
|
37
|
+
puts client.get('/me').username
|
38
|
+
|
39
|
+
#### Do the OAuth2 authorization code flow
|
40
|
+
sc = Soundcloud.new({
|
41
|
+
:client_id => 'YOUR_CLIENT_ID',
|
42
|
+
:client_secret => 'YOUR_CLIENT_SECRET',
|
43
|
+
})
|
44
|
+
|
45
|
+
sc.authorize_url(:redirect_uri => uri)
|
46
|
+
# => "https://soundcloud.com/connect?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http://host/redirect"
|
47
|
+
sc.exchange_code(:redirect_uri => uri, :code => 'CODE')
|
48
|
+
|
49
|
+
#### Do the OAuth2 refresh token flow, upload a track and print its link
|
50
|
+
# register a new client which will exchange an existing refresh_token for an access_token
|
51
|
+
client = Soundcloud.new({
|
52
|
+
:client_id => 'YOUR_CLIENT_ID',
|
53
|
+
:client_secret => 'YOUR_CLIENT_SECRET',
|
54
|
+
:refresh_token => 'SOME_REFRESH_TOKEN'
|
55
|
+
})
|
56
|
+
|
57
|
+
# upload a new track with track.mp3 as audio and image.jpg as artwork
|
58
|
+
track = client.post('/tracks', {
|
59
|
+
:title => 'a new track',
|
60
|
+
:asset_data => File.new('track.mp3'),
|
61
|
+
:artwork_data => File.new('image.jpg')
|
62
|
+
})
|
63
|
+
|
64
|
+
# print new tracks link
|
65
|
+
puts track.permalink_url
|
66
|
+
|
67
|
+
#### Resolve a track url and print its id
|
68
|
+
# register the client
|
69
|
+
client = Soundcloud.new(:client_id => 'YOUR_CLIENT_ID')
|
70
|
+
|
71
|
+
# call the resolve endpoint with a track url
|
72
|
+
track = client.get('/resolve', :url => "http://soundcloud.com/forss/flickermood")
|
73
|
+
|
74
|
+
# print the track id
|
75
|
+
puts track.id
|
76
|
+
|
77
|
+
#### Register a client for http://sandbox-soundcloud.com with an existing access_token and start following a user
|
78
|
+
# register a client for http://sandbox-soundcloud.com with existing access_token
|
79
|
+
client = Soundcloud.new(:site => 'http://sandbox-soundcloud.com', :access_token => 'SOME_ACCESS_TOKEN')
|
80
|
+
|
81
|
+
# create a new following
|
82
|
+
user_id_to_follow = 123
|
83
|
+
client.put("/me/followings/#{user_id_to_follow}")
|
84
|
+
|
85
|
+
## Details
|
86
|
+
#### Soundcloud.new(options={})
|
87
|
+
Will store the passed options and call exchange_token in case options are passed that allow an exchange of tokens.
|
88
|
+
|
89
|
+
#### Soundcloud#exchange_token(options={})
|
90
|
+
Will store the passed options and try to exchange tokens if no access_token is present and:
|
91
|
+
- refresh_token, client_id and client_secret is present.
|
92
|
+
- client_id, client_secret, username, password is present
|
93
|
+
- client_id, client_secret, redirect_uri, code is present
|
94
|
+
|
95
|
+
#### Soundcloud#authorize_url(options={})
|
96
|
+
Will store the passed options and return an authorize url.
|
97
|
+
The client_id and redirect_uri options need to present to generate the authorize url.
|
98
|
+
|
99
|
+
#### Soundcloud#get, Soundcloud#post, Soundcloud#put, Soundcloud#delete, Soundcloud#head
|
100
|
+
All available HTTP methods are exposed through these methods. They all share the signature (path_or_uri, query={}, options={}).
|
101
|
+
The query hash will be merged with the options hash and passed to httparty. Depending on if the client is authorized it will either add the client_id or the access_token as a query parameter.
|
102
|
+
In case an access_token is expired and a refresh_token is present it will try to refresh the access_token and retry the call.
|
103
|
+
The response is either a Hashie::Mash or an array of Hashie::Mashs. The mashs expose all resource attributes as methods and the original response through #response.
|
104
|
+
|
105
|
+
#### Soundcloud#client_id, client_secret, access_token, refresh_token, use_ssl?
|
106
|
+
These are accessor to the stored options.
|
107
|
+
|
108
|
+
#### Error Handling
|
109
|
+
In case a request was not successful a Soundcloud::ResponseError will be raise.
|
110
|
+
The original HTTParty response is available through Soundcloud::ResponseError#response.
|
data/lib/soundcloud.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
gem 'httmultiparty'
|
2
|
+
gem 'mash'
|
3
|
+
require 'httmultiparty'
|
4
|
+
require 'hashie'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
class Soundcloud
|
8
|
+
class ResponseError < HTTParty::ResponseError; end
|
9
|
+
include HTTMultiParty
|
10
|
+
headers 'Accept' => 'application/json'
|
11
|
+
|
12
|
+
# TODO fix when api is ready for client_id
|
13
|
+
CLIENT_ID_PARAM_NAME = :consumer_key
|
14
|
+
API_SUBHOST = 'api'
|
15
|
+
AUTHORIZE_PATH = '/connect'
|
16
|
+
TOKEN_PATH = '/oauth2/token'
|
17
|
+
DEFAULT_OPTIONS = {
|
18
|
+
:site => 'soundcloud.com'
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
def initialize(options={})
|
23
|
+
store_options(options)
|
24
|
+
if access_token.nil? && (options_for_refresh_flow_present? ||
|
25
|
+
options_for_credentials_flow_present? || options_for_code_flow_present?)
|
26
|
+
exchange_token
|
27
|
+
end
|
28
|
+
|
29
|
+
raise ArgumentError, "At least a client_id or an access_token must be present" if client_id.nil? && access_token.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def get (path, query={}, options={}); handle_response { self.class.get *construct_query_arguments(path, options.merge(:query => query)) } end
|
33
|
+
def post (path, query={}, options={}); handle_response { self.class.post *construct_query_arguments(path, options.merge(:query => query)) } end
|
34
|
+
def put (path, query={}, options={}); handle_response { self.class.put *construct_query_arguments(path, options.merge(:query => query)) } end
|
35
|
+
def delete(path, query={}, options={}); handle_response { self.class.delete *construct_query_arguments(path, options.merge(:query => query)) } end
|
36
|
+
def head (path, query={}, options={}); handle_response { self.class.head *construct_query_arguments(path, options.merge(:query => query)) } end
|
37
|
+
|
38
|
+
# accessors for options
|
39
|
+
def client_id; @options[:client_id]; end
|
40
|
+
def client_secret; @options[:client_secret]; end
|
41
|
+
def access_token; @options[:access_token]; end
|
42
|
+
def refresh_token; @options[:refresh_token]; end
|
43
|
+
def use_ssl?;
|
44
|
+
!! @options[:use_ssl?] || access_token
|
45
|
+
end
|
46
|
+
|
47
|
+
def site; @options[:site]; end
|
48
|
+
|
49
|
+
def host; site; end
|
50
|
+
def api_host; [API_SUBHOST, host].join('.'); end
|
51
|
+
|
52
|
+
def authorize_url(options={})
|
53
|
+
store_options(options)
|
54
|
+
"https://#{host}#{AUTHORIZE_PATH}?response_type=code&client_id=#{client_id}&redirect_uri=#{URI.escape redirect_uri}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def exchange_token(options={})
|
58
|
+
store_options(options)
|
59
|
+
raise ArgumentError, 'client_id and client_secret is required to retrieve an access_token' if client_id.nil? || client_secret.nil?
|
60
|
+
client_params = {:client_id => client_id, :client_secret => client_secret}
|
61
|
+
params = if options_for_refresh_flow_present?
|
62
|
+
{:grant_type => 'refresh_token', :refresh_token => refresh_token}
|
63
|
+
elsif options_for_credentials_flow_present?
|
64
|
+
{:grant_type => 'password', :username => @options[:username], :password => @options[:password]}
|
65
|
+
elsif options_for_code_flow_present?
|
66
|
+
{:grant_type => 'authorization_code', :redirect_uri => @options[:redirect_uri], :code => @options[:code]}
|
67
|
+
end
|
68
|
+
params.merge!(client_params)
|
69
|
+
response = handle_response {
|
70
|
+
self.class.post("https://#{api_host}#{TOKEN_PATH}", :query => params)
|
71
|
+
}
|
72
|
+
@options.merge!(:access_token => response.access_token, :refresh_token => response.refresh_token)
|
73
|
+
response
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def handle_response(refreshing_enabled=true, &block)
|
78
|
+
response = block.call
|
79
|
+
if response && !response.success?
|
80
|
+
if response.code == 401 && response["error"] == "invalid_grant" && refreshing_enabled
|
81
|
+
exchange_token
|
82
|
+
# TODO it should return the original
|
83
|
+
handle_response(false, &block)
|
84
|
+
else
|
85
|
+
raise ResponseError.new(response), "HTTP Status #{response.code}"
|
86
|
+
end
|
87
|
+
elsif response.is_a? Hash
|
88
|
+
HashResponseWrapper.new(response)
|
89
|
+
elsif response.is_a? Array
|
90
|
+
ArrayResponseWrapper.new(response)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def options_for_refresh_flow_present?; !! @options[:refresh_token]; end
|
95
|
+
def options_for_credentials_flow_present?; !! @options[:username] && @options[:password]; end
|
96
|
+
def options_for_code_flow_present?; !! @options[:code] && @options[:redirect_uri]; end
|
97
|
+
|
98
|
+
def store_options(options={})
|
99
|
+
@options ||= DEFAULT_OPTIONS.dup
|
100
|
+
@options.merge! options
|
101
|
+
end
|
102
|
+
|
103
|
+
def construct_query_arguments(path_or_uri, options={})
|
104
|
+
path = URI.parse(path_or_uri).path
|
105
|
+
scheme = use_ssl? ? 'https' : 'http'
|
106
|
+
options = options.dup
|
107
|
+
options[:query] ||= {}
|
108
|
+
|
109
|
+
if access_token
|
110
|
+
options[:query][:oauth_token] = access_token
|
111
|
+
else
|
112
|
+
options[:query][CLIENT_ID_PARAM_NAME] = client_id
|
113
|
+
end
|
114
|
+
[
|
115
|
+
"#{scheme}://#{api_host}#{path}",
|
116
|
+
options
|
117
|
+
]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
require 'soundcloud/array_response_wrapper'
|
122
|
+
require 'soundcloud/hash_response_wrapper'
|
123
|
+
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: soundcloud
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 1452983230
|
5
|
+
prerelease: true
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 3b
|
10
|
+
version: 0.1.3b
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Johannes Wagener
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-26 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: httparty
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 5
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 7
|
33
|
+
- 3
|
34
|
+
version: 0.7.3
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: httmultiparty
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 2
|
49
|
+
version: "0.2"
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: hashie
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :runtime
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: rspec
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: fakeweb
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
type: :development
|
93
|
+
version_requirements: *id005
|
94
|
+
description: A simple Soundcloud API wrapper based of httparty, multipart-post, httmultiparty
|
95
|
+
email:
|
96
|
+
- johannes@soundcloud.com
|
97
|
+
executables: []
|
98
|
+
|
99
|
+
extensions: []
|
100
|
+
|
101
|
+
extra_rdoc_files: []
|
102
|
+
|
103
|
+
files:
|
104
|
+
- lib/soundcloud/array_response_wrapper.rb
|
105
|
+
- lib/soundcloud/hash_response_wrapper.rb
|
106
|
+
- lib/soundcloud/version.rb
|
107
|
+
- lib/soundcloud.rb
|
108
|
+
- README.md
|
109
|
+
has_rdoc: true
|
110
|
+
homepage: http://dev.soundcloud.com
|
111
|
+
licenses: []
|
112
|
+
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
hash: 23
|
133
|
+
segments:
|
134
|
+
- 1
|
135
|
+
- 3
|
136
|
+
- 6
|
137
|
+
version: 1.3.6
|
138
|
+
requirements: []
|
139
|
+
|
140
|
+
rubyforge_project: soundcloud
|
141
|
+
rubygems_version: 1.3.7
|
142
|
+
signing_key:
|
143
|
+
specification_version: 3
|
144
|
+
summary: A simple Soundcloud API wrapper
|
145
|
+
test_files: []
|
146
|
+
|