rdio-cli 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/CONTRIBUTING.md +18 -0
- data/LICENSE.md +20 -0
- data/README.md +87 -0
- data/Rakefile +8 -0
- data/bin/rdio +4 -0
- data/lib/api.rb +82 -0
- data/lib/om.rb +160 -0
- data/lib/rdio/version.rb +3 -0
- data/lib/rdio.rb +282 -0
- data/rdio-cli.gemspec +27 -0
- metadata +174 -0
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
## Contributing
|
2
|
+
In the spirit of [free software][free-sw], **everyone** is encouraged to help
|
3
|
+
improve this project.
|
4
|
+
|
5
|
+
## Submitting a Pull Request
|
6
|
+
1. [Fork the repository.][fork]
|
7
|
+
2. [Create a topic branch.][branch]
|
8
|
+
3. Add specs for your unimplemented feature or bug fix.
|
9
|
+
4. Run `bundle exec rake spec`. If your specs pass, return to step 3.
|
10
|
+
5. Implement your feature or bug fix.
|
11
|
+
6. Run `bundle exec rake spec`. If your specs fail, return to step 5.
|
12
|
+
7. Add, commit, and push your changes.
|
13
|
+
8. [Submit a pull request.][pr]
|
14
|
+
|
15
|
+
[free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
|
16
|
+
[fork]: http://help.github.com/fork-a-repo/
|
17
|
+
[branch]: http://learn.github.com/p/branching.html
|
18
|
+
[pr]: http://help.github.com/send-pull-requests/
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Wynn Netherland
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Rdio CLI
|
2
|
+
|
3
|
+
A simple command line interface for [Rdio][].
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```
|
8
|
+
gem install rdio-cli
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Rdio CLI is powered by [GLI][] and has a Git-like (sub)command interface:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
$ rdio current
|
17
|
+
Now playing: All The Roadrunning / Mark Knopfler And Emmylou Harris / Real Live Roadrunning
|
18
|
+
```
|
19
|
+
|
20
|
+
```bash
|
21
|
+
$ rdio current "♫ %{track} ♫"
|
22
|
+
♫ All The Roadrunning ♫
|
23
|
+
```
|
24
|
+
|
25
|
+
### Full usage help
|
26
|
+
|
27
|
+
```shell
|
28
|
+
|
29
|
+
$ rdio help
|
30
|
+
|
31
|
+
NAME
|
32
|
+
rdio - Simple CLI for Rdio
|
33
|
+
|
34
|
+
SYNOPSIS
|
35
|
+
rdio [global options] command [command options] [arguments...]
|
36
|
+
|
37
|
+
VERSION
|
38
|
+
0.0.1
|
39
|
+
|
40
|
+
GLOBAL OPTIONS
|
41
|
+
--access_secret=arg - (default: none)
|
42
|
+
--access_token=arg - (default: none)
|
43
|
+
--consumer_key=arg - (default: none)
|
44
|
+
--consumer_secret=arg - (default: none)
|
45
|
+
--help - Show this message
|
46
|
+
--version -
|
47
|
+
|
48
|
+
COMMANDS
|
49
|
+
authorize, auth - Authorize Rdio account
|
50
|
+
browse - Open the current track in Rdio player
|
51
|
+
current - Display the current track info
|
52
|
+
help - Shows a list of commands or help for one command
|
53
|
+
initconfig - Initialize the config file using current global options
|
54
|
+
link - Get a shareable link for the current track
|
55
|
+
mute - Mute the Rdio player
|
56
|
+
next - Skip to next track
|
57
|
+
pause - Pause the player
|
58
|
+
play - Plays the current track
|
59
|
+
previous, prev - Play previous track
|
60
|
+
quit, q - Quit Rdio
|
61
|
+
toggle - Toggle playback
|
62
|
+
user - Show the current Rdio user
|
63
|
+
version, v - Get CLI and application version info
|
64
|
+
volume, vol - Set volume for player
|
65
|
+
```
|
66
|
+
|
67
|
+
## TODO
|
68
|
+
* `[✓]` <del>Snag current track to collection</del>
|
69
|
+
* `[ ]` Snag current album to collection
|
70
|
+
* `[ ]` Create a playlist
|
71
|
+
* `[ ]` Follow a user
|
72
|
+
* `[ ]` Tail a user?
|
73
|
+
|
74
|
+
## Credits
|
75
|
+
|
76
|
+
* Uses Rdio's [rdio-simple][] library for API access.
|
77
|
+
* Inspired by [Drew Stokes][]'s [Node version][node-rdio].
|
78
|
+
|
79
|
+
## Copyright
|
80
|
+
Copyright (c) 2012 Wynn Netherland. See [LICENSE][] for details.
|
81
|
+
|
82
|
+
[rdio]: http://rdio.com
|
83
|
+
[LICENSE]: https://github.com/pengwynn/rdio-cli/blob/master/LICENSE.md
|
84
|
+
[rdio-simple]: https://github.com/rdio/rdio-simple
|
85
|
+
[Drew Stokes]: https://github.com/dstokes
|
86
|
+
[node-rdio]: https://github.com/dstokes/rdio-cli
|
87
|
+
[GLI]: https://github.com/davetron5000/gli
|
data/Rakefile
ADDED
data/bin/rdio
ADDED
data/lib/api.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# (c) 2011 Rdio Inc
|
2
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
3
|
+
# of this software and associated documentation files (the "Software"), to deal
|
4
|
+
# in the Software without restriction, including without limitation the rights
|
5
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
6
|
+
# copies of the Software, and to permit persons to whom the Software is
|
7
|
+
# furnished to do so, subject to the following conditions:
|
8
|
+
#
|
9
|
+
# The above copyright notice and this permission notice shall be included in
|
10
|
+
# all copies or substantial portions of the Software.
|
11
|
+
#
|
12
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
13
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
14
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
15
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
16
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
17
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
18
|
+
# THE SOFTWARE.
|
19
|
+
|
20
|
+
require 'om'
|
21
|
+
require 'uri'
|
22
|
+
require 'net/http'
|
23
|
+
require 'cgi'
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
class Api
|
27
|
+
# the consumer and token can be accessed
|
28
|
+
attr_accessor :consumer, :token
|
29
|
+
|
30
|
+
def initialize(consumer, token=nil)
|
31
|
+
@consumer = consumer
|
32
|
+
@token = token
|
33
|
+
end
|
34
|
+
|
35
|
+
def begin_authentication(callback_url)
|
36
|
+
# request a request token from the server
|
37
|
+
response = signed_post('http://api.rdio.com/oauth/request_token',
|
38
|
+
{'oauth_callback' => callback_url})
|
39
|
+
# parse the response
|
40
|
+
parsed = CGI.parse(response)
|
41
|
+
# save the token
|
42
|
+
@token = [parsed['oauth_token'][0], parsed['oauth_token_secret'][0]]
|
43
|
+
# return an URL that the user can use to authorize this application
|
44
|
+
return parsed['login_url'][0] + '?oauth_token=' + parsed['oauth_token'][0]
|
45
|
+
end
|
46
|
+
|
47
|
+
def complete_authentication(verifier)
|
48
|
+
# request an access token
|
49
|
+
response = signed_post('http://api.rdio.com/oauth/access_token',
|
50
|
+
{'oauth_verifier' => verifier})
|
51
|
+
# parse the response
|
52
|
+
parsed = CGI.parse(response)
|
53
|
+
# save the token
|
54
|
+
@token = [parsed['oauth_token'][0], parsed['oauth_token_secret'][0]]
|
55
|
+
end
|
56
|
+
|
57
|
+
def call(method, params={})
|
58
|
+
# make a copy of the dict
|
59
|
+
params = params.clone
|
60
|
+
# put the method in the dict
|
61
|
+
params['method'] = method
|
62
|
+
# call to the server and parse the response
|
63
|
+
return JSON.load(signed_post('http://api.rdio.com/1/', params))
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def signed_post(url, params)
|
69
|
+
auth = om(@consumer, url, params, @token)
|
70
|
+
url = URI.parse(url)
|
71
|
+
http = Net::HTTP.new(url.host, url.port)
|
72
|
+
req = Net::HTTP::Post.new(url.path, {'Authorization' => auth})
|
73
|
+
req.set_form_data(params)
|
74
|
+
res = http.request(req)
|
75
|
+
return res.body
|
76
|
+
end
|
77
|
+
|
78
|
+
def method_missing(method, *params)
|
79
|
+
call(method.to_s, params[0])
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/lib/om.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# om is oauth-mini - a simple implementation of a useful subset of OAuth.
|
2
|
+
# It's designed to be useful and reusable but not general purpose.
|
3
|
+
#
|
4
|
+
# (c) 2011 Rdio Inc
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
# A simple OAuth client implementation. Do less better.
|
24
|
+
# Here are the restrictions:
|
25
|
+
# - only HMAC-SHA1 is supported
|
26
|
+
# - only WWW-Authentiate form signatures are generated
|
27
|
+
#
|
28
|
+
# To sign a request:
|
29
|
+
# auth = om([consumer_key,consumer_secret], url, params)
|
30
|
+
# send Authorization: <auth>
|
31
|
+
# when POSTing <params> to <url>
|
32
|
+
# Optional additional arguments are:
|
33
|
+
# token = [oauth_token, oauth_token_secret]
|
34
|
+
# method = "POST"
|
35
|
+
# realm = "Realm-for-authorization-header"
|
36
|
+
|
37
|
+
require 'uri'
|
38
|
+
require 'cgi'
|
39
|
+
require 'digest'
|
40
|
+
require 'digest/sha1'
|
41
|
+
|
42
|
+
if not "".respond_to?(:encoding)
|
43
|
+
# ruby 1.8 doesn't know about unicode :(
|
44
|
+
require 'iconv'
|
45
|
+
# we will just check that bytes are valid UTF-8
|
46
|
+
$__om_utf8_checker = Iconv.new("UTF-8", "UTF-8")
|
47
|
+
end
|
48
|
+
|
49
|
+
def om(consumer, url, post_params, token=nil, method='POST', realm=nil, timestamp=nil, nonce=nil)
|
50
|
+
# A one-shot simple OAuth signature generator
|
51
|
+
|
52
|
+
# the method must be upper-case
|
53
|
+
method = method.upcase
|
54
|
+
|
55
|
+
# we want params as an Array of name / value pairs
|
56
|
+
if post_params.is_a?(Array)
|
57
|
+
params = post_params
|
58
|
+
else
|
59
|
+
params = post_params.collect { |x| x }
|
60
|
+
end
|
61
|
+
|
62
|
+
# we want those pairs to be strings
|
63
|
+
params = params.collect { |k,v| [k.to_s, v.to_s]}
|
64
|
+
|
65
|
+
# normalize the URL
|
66
|
+
url = URI.parse(url)
|
67
|
+
# scheme is lower-case
|
68
|
+
url.scheme = url.scheme.downcase
|
69
|
+
# remove username & password
|
70
|
+
url.user = url.password = nil
|
71
|
+
# host is lowercase
|
72
|
+
url.host = url.host.downcase
|
73
|
+
|
74
|
+
# add URL params to the params
|
75
|
+
if url.query
|
76
|
+
CGI.parse(url.query).each { |k,vs| vs.each { |v| params.push([k,v]) } }
|
77
|
+
end
|
78
|
+
|
79
|
+
# remove the params and fragment
|
80
|
+
url.query = nil
|
81
|
+
url.fragment = nil
|
82
|
+
|
83
|
+
# add OAuth params
|
84
|
+
params = params + [
|
85
|
+
['oauth_version', '1.0'],
|
86
|
+
['oauth_timestamp', timestamp || Time.now.to_i.to_s],
|
87
|
+
['oauth_nonce', nonce || rand(1000000).to_s],
|
88
|
+
['oauth_signature_method', 'HMAC-SHA1'],
|
89
|
+
['oauth_consumer_key', consumer[0]],
|
90
|
+
]
|
91
|
+
|
92
|
+
# the consumer secret is the first half of the HMAC-SHA1 key
|
93
|
+
hmac_key = consumer[1] + '&'
|
94
|
+
|
95
|
+
if token != nil
|
96
|
+
# include a token in params
|
97
|
+
params.push ['oauth_token', token[0]]
|
98
|
+
# and the token secret in the HMAC-SHA1 key
|
99
|
+
hmac_key += token[1]
|
100
|
+
end
|
101
|
+
|
102
|
+
def percent_encode(s)
|
103
|
+
if s.respond_to?(:encoding)
|
104
|
+
# Ruby 1.9 knows about encodings, convert the string to UTF-8
|
105
|
+
s = s.encode(Encoding::UTF_8)
|
106
|
+
else
|
107
|
+
# Ruby 1.8 does not, just check that it's valid UTF-8
|
108
|
+
begin
|
109
|
+
$__om_utf8_checker.iconv(s)
|
110
|
+
rescue Iconv::IllegalSequence => exception
|
111
|
+
throw ArgumentError.new("Non-UTF-8 string: "+s.inspect)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
chars = s.bytes.map do |b|
|
115
|
+
c = b.chr
|
116
|
+
if ((c >= '0' and c <= '9') or
|
117
|
+
(c >= 'A' and c <= 'Z') or
|
118
|
+
(c >= 'a' and c <= 'z') or
|
119
|
+
c == '-' or c == '.' or c == '_' or c == '~')
|
120
|
+
c
|
121
|
+
else
|
122
|
+
'%%%02X' % b
|
123
|
+
end
|
124
|
+
end
|
125
|
+
chars.join
|
126
|
+
end
|
127
|
+
|
128
|
+
# Sort lexicographically, first after key, then after value.
|
129
|
+
params.sort!
|
130
|
+
# escape the key/value pairs and combine them into a string
|
131
|
+
normalized_params = (params.collect {|p| percent_encode(p[0])+'='+percent_encode(p[1])}).join '&'
|
132
|
+
|
133
|
+
# build the signature base string
|
134
|
+
signature_base_string = (percent_encode(method) +
|
135
|
+
'&' + percent_encode(url.to_s) +
|
136
|
+
'&' + percent_encode(normalized_params))
|
137
|
+
|
138
|
+
# HMAC-SHA1
|
139
|
+
hmac = Digest::HMAC.new(hmac_key, Digest::SHA1)
|
140
|
+
hmac.update(signature_base_string)
|
141
|
+
|
142
|
+
# Calculate the digest base 64. Drop the trailing \n
|
143
|
+
oauth_signature = [hmac.digest].pack('m0').strip
|
144
|
+
|
145
|
+
# Build the Authorization header
|
146
|
+
if realm
|
147
|
+
authorization_params = [['realm', realm]]
|
148
|
+
else
|
149
|
+
authorization_params = []
|
150
|
+
end
|
151
|
+
authorization_params.push(['oauth_signature', oauth_signature])
|
152
|
+
|
153
|
+
# we only want certain params in the auth header
|
154
|
+
oauth_params = ['oauth_version', 'oauth_timestamp', 'oauth_nonce',
|
155
|
+
'oauth_signature_method', 'oauth_signature',
|
156
|
+
'oauth_consumer_key', 'oauth_token']
|
157
|
+
authorization_params.concat(params.select { |param| nil != oauth_params.index(param[0]) })
|
158
|
+
|
159
|
+
return 'OAuth ' + (authorization_params.collect {|param| '%s="%s"' % param}).join(', ')
|
160
|
+
end
|
data/lib/rdio/version.rb
ADDED
data/lib/rdio.rb
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'gli'
|
2
|
+
require 'yaml'
|
3
|
+
require 'launchy'
|
4
|
+
|
5
|
+
require 'api'
|
6
|
+
require 'rdio/version'
|
7
|
+
require 'highline/import'
|
8
|
+
|
9
|
+
module Rdio
|
10
|
+
extend GLI::App
|
11
|
+
|
12
|
+
program_desc 'Simple CLI for Rdio'
|
13
|
+
|
14
|
+
version Rdio::VERSION
|
15
|
+
|
16
|
+
def self.api
|
17
|
+
token = @access_token ? [@access_token, @access_secret] : nil
|
18
|
+
@api = Api.new \
|
19
|
+
[@consumer_key, @consumer_secret],
|
20
|
+
token
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.authorize_api
|
24
|
+
url = api.begin_authentication('oob')
|
25
|
+
ask "Copy the four digit code from your browser. [ENTER] to continue. "
|
26
|
+
Launchy.open url
|
27
|
+
code = ask 'Code: '
|
28
|
+
@access_token, @access_secret = @api.complete_authentication(code)
|
29
|
+
|
30
|
+
write_config
|
31
|
+
|
32
|
+
say "You're all set. see `rdio help` for usage"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.rdio_config
|
36
|
+
{
|
37
|
+
:consumer_key => @consumer_key,
|
38
|
+
:consumer_secret => @consumer_secret,
|
39
|
+
:access_token => @access_token,
|
40
|
+
:access_secret => @access_secret
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.write_config
|
45
|
+
p = File.join(File.expand_path(ENV['HOME']), '.rdio')
|
46
|
+
File.open(p, 'w' ) do |out|
|
47
|
+
YAML.dump rdio_config, out
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.apple_script(cmd)
|
52
|
+
`osascript -e '#{cmd}'`
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.tell_rdio(cmd)
|
56
|
+
apple_script "tell app \"Rdio\" to #{cmd}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.current_track
|
60
|
+
tell_rdio('name of the current track').gsub(/\n/, '')
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.current_artist
|
64
|
+
tell_rdio('artist of the current track').gsub(/\n/, '')
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.current_album
|
68
|
+
tell_rdio('album of the current track').gsub(/\n/, '')
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.display_track(text)
|
72
|
+
text = "Now playing: %{track} / %{artist} / %{album}" if text.nil?
|
73
|
+
say (text % {
|
74
|
+
:artist => current_artist,
|
75
|
+
:track => current_track,
|
76
|
+
:album => current_album
|
77
|
+
})
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.set_volume(pct = 30)
|
81
|
+
tell_rdio "set the sound volume to #{pct}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.rdio_url
|
85
|
+
path = tell_rdio 'rdio url of the current track'
|
86
|
+
"http://www.rdio.com#{path}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.current_track_key
|
90
|
+
data = api.call 'getObjectFromUrl', { :url => rdio_url }
|
91
|
+
|
92
|
+
data['result']['key']
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.add_to_collection(tracks)
|
96
|
+
tracks = Array(tracks)
|
97
|
+
|
98
|
+
api.call 'addToCollection', { :keys => tracks.join(',') }
|
99
|
+
end
|
100
|
+
|
101
|
+
config_file '.rdio'
|
102
|
+
|
103
|
+
flag :consumer_key
|
104
|
+
flag :consumer_secret
|
105
|
+
flag :access_token
|
106
|
+
flag :access_secret
|
107
|
+
|
108
|
+
desc 'Plays the current track'
|
109
|
+
command :play do |c|
|
110
|
+
c.action do |global_options,options,args|
|
111
|
+
tell_rdio "play"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'Pause the player'
|
116
|
+
skips_pre
|
117
|
+
command :pause do |c|
|
118
|
+
c.action do |global_options,options,args|
|
119
|
+
tell_rdio "pause"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
desc 'Toggle playback'
|
124
|
+
skips_pre
|
125
|
+
command :toggle do |c|
|
126
|
+
c.action do |global_options,options,args|
|
127
|
+
tell_rdio "playpause"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
desc 'Display the current track info'
|
132
|
+
long_desc %(
|
133
|
+
Display current track, artist, and album info. Pass
|
134
|
+
format string for custom output using
|
135
|
+
%{track}, %{artist}, and %{album} placeholders.
|
136
|
+
)
|
137
|
+
arg_name 'format'
|
138
|
+
skips_pre
|
139
|
+
command :current do |c|
|
140
|
+
c.action do |global_options,options,args|
|
141
|
+
display_track args.first
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
desc 'Skip to next track'
|
146
|
+
skips_pre
|
147
|
+
command :next do |c|
|
148
|
+
c.action do |global_options,options,args|
|
149
|
+
tell_rdio "next track"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
desc 'Play previous track'
|
154
|
+
skips_pre
|
155
|
+
command :previous, :prev do |c|
|
156
|
+
c.action do |global_options,options,args|
|
157
|
+
tell_rdio "previous track"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
desc 'Open the current track in Rdio player'
|
162
|
+
skips_pre
|
163
|
+
command :browse do |c|
|
164
|
+
c.action do |global_options,options,args|
|
165
|
+
exec "open '#{rdio_url}'"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
desc 'Set volume for player'
|
170
|
+
skips_pre
|
171
|
+
arg_name 'level'
|
172
|
+
command :volume, :vol do |c|
|
173
|
+
c.action do |global_options,options,args|
|
174
|
+
level = args.shift.to_i
|
175
|
+
set_volume level
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
desc 'Mute the Rdio player'
|
180
|
+
skips_pre
|
181
|
+
command :mute do |c|
|
182
|
+
c.action do |global_options,options,args|
|
183
|
+
set_volume 0
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
desc 'Get a shareable link for the current track'
|
188
|
+
skips_pre
|
189
|
+
command :link do |c|
|
190
|
+
c.action do |global_options,options,args|
|
191
|
+
say rdio_url
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
desc "Get CLI and application version info"
|
196
|
+
skips_pre
|
197
|
+
command :version, :v do |c|
|
198
|
+
c.action do |global_options,options,args|
|
199
|
+
say "rdio-cli #{Rdio::VERSION} / Rdio #{apple_script('get version of application "Rdio"')}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
desc "Quit Rdio"
|
204
|
+
skips_pre
|
205
|
+
command :quit, :q do |c|
|
206
|
+
c.action do |global_options,options,args|
|
207
|
+
apple_script "tell application \"Rdio\" to quit"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
desc "Authorize Rdio account"
|
212
|
+
skips_pre
|
213
|
+
command :authorize, :auth do |c|
|
214
|
+
c.action do |global_options,options,args|
|
215
|
+
require 'highline/import'
|
216
|
+
say "To access your Rdio account, you'll need to get some API keys. "
|
217
|
+
say "See http://developer.rdio.com for details. "
|
218
|
+
@consumer_key = ask 'Enter your Rdio API key: '
|
219
|
+
@consumer_secret = ask 'Enter your Rdio API secret: '
|
220
|
+
|
221
|
+
if @consumer_key && @consumer_secret
|
222
|
+
authorize_api
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
### Authenticated methods
|
228
|
+
|
229
|
+
desc 'Show the current Rdio user'
|
230
|
+
command :user do |c|
|
231
|
+
c.action do |global_options,options,args|
|
232
|
+
user = api.call('currentUser')['result']
|
233
|
+
say "#{user['firstName']} #{user['lastName']}"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
desc 'Add the current track or album to your collection'
|
238
|
+
command :snag do |c|
|
239
|
+
c.action do |global_options,options,args|
|
240
|
+
case args.shift
|
241
|
+
when 'album'
|
242
|
+
say 'Not implemented'
|
243
|
+
when nil
|
244
|
+
add_to_collection current_track_key
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
pre do |global,command,options,args|
|
250
|
+
# Pre logic here
|
251
|
+
# Return true to proceed; false to abourt and not call the
|
252
|
+
# chosen command
|
253
|
+
# Use skips_pre before a command to skip this block
|
254
|
+
# on that command only
|
255
|
+
@consumer_key = global[:consumer_key]
|
256
|
+
@consumer_secret = global[:consumer_secret]
|
257
|
+
@access_token = global[:access_token]
|
258
|
+
@access_secret = global[:access_secret]
|
259
|
+
if api.token.compact.empty?
|
260
|
+
say 'Rdio credentials not found. Please run: rdio authorize'
|
261
|
+
|
262
|
+
false
|
263
|
+
else
|
264
|
+
true
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
post do |global,command,options,args|
|
269
|
+
# Post logic here
|
270
|
+
# Use skips_post before a command to skip this
|
271
|
+
# block on that command only
|
272
|
+
end
|
273
|
+
|
274
|
+
on_error do |exception|
|
275
|
+
# Error logic here
|
276
|
+
# return false to skip default error handling
|
277
|
+
true
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
|
data/rdio-cli.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Ensure we require the local version and not one we might have installed already
|
2
|
+
require File.join([File.dirname(__FILE__),'lib','rdio','version.rb'])
|
3
|
+
spec = Gem::Specification.new do |s|
|
4
|
+
s.name = 'rdio-cli'
|
5
|
+
s.version = Rdio::VERSION
|
6
|
+
s.author = 'Wynn Netherland'
|
7
|
+
s.email = 'wynn.netherland@gmail.com'
|
8
|
+
s.homepage = 'http://wynnnetherland.com'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = 'CLI for Rdio for Mac'
|
11
|
+
s.files = %w(CONTRIBUTING.md LICENSE.md README.md Rakefile rdio-cli.gemspec)
|
12
|
+
s.files += Dir.glob("lib/**/*.rb")
|
13
|
+
s.files += Dir.glob("bin/**/*")
|
14
|
+
s.files += Dir.glob("etc/**/*")
|
15
|
+
s.require_paths << 'lib'
|
16
|
+
s.has_rdoc = false
|
17
|
+
s.rdoc_options << '--title' << 'rdio' << '--main' << 'README.rdoc' << '-ri'
|
18
|
+
s.bindir = 'bin'
|
19
|
+
s.executables << 'rdio'
|
20
|
+
s.add_dependency('highline', '~> 1.6.15')
|
21
|
+
s.add_dependency('launchy', '~> 2.1.2')
|
22
|
+
s.add_development_dependency('rake')
|
23
|
+
s.add_development_dependency('rdoc')
|
24
|
+
s.add_development_dependency('rspec', '~> 2.12.0')
|
25
|
+
s.add_development_dependency('rspec-mocks', '~> 2.12.0')
|
26
|
+
s.add_runtime_dependency('gli','2.5.2')
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdio-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Wynn Netherland
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: highline
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.15
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.15
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: launchy
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.1.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.1.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rdoc
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.12.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.12.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec-mocks
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.12.0
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.12.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: gli
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.5.2
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - '='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.5.2
|
126
|
+
description:
|
127
|
+
email: wynn.netherland@gmail.com
|
128
|
+
executables:
|
129
|
+
- rdio
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- CONTRIBUTING.md
|
134
|
+
- LICENSE.md
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- rdio-cli.gemspec
|
138
|
+
- lib/api.rb
|
139
|
+
- lib/om.rb
|
140
|
+
- lib/rdio/version.rb
|
141
|
+
- lib/rdio.rb
|
142
|
+
- bin/rdio
|
143
|
+
homepage: http://wynnnetherland.com
|
144
|
+
licenses: []
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options:
|
147
|
+
- --title
|
148
|
+
- rdio
|
149
|
+
- --main
|
150
|
+
- README.rdoc
|
151
|
+
- -ri
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ! '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubyforge_project:
|
169
|
+
rubygems_version: 1.8.23
|
170
|
+
signing_key:
|
171
|
+
specification_version: 3
|
172
|
+
summary: CLI for Rdio for Mac
|
173
|
+
test_files: []
|
174
|
+
has_rdoc: false
|