soundcloud 0.1.3b

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -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
+
@@ -0,0 +1,8 @@
1
+ class Soundcloud::ArrayResponseWrapper < Array
2
+ attr_reader :response
3
+ def initialize(response)
4
+ mashes = response.map { |o| Hashie::Mash.new(o) }
5
+ self.replace(mashes)
6
+ @response = response
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class Soundcloud::HashResponseWrapper < Hashie::Mash
2
+ attr_reader :response
3
+ def initialize(response)
4
+ super(response)
5
+ @response = response
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class Soundcloud
2
+ VERSION = '0.1.3b'
3
+ end
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
+