rdio-cli 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|