spotify_cli 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/spotify_cli/api.rb +59 -60
- data/lib/spotify_cli/app.rb +117 -83
- data/lib/spotify_cli/version.rb +1 -1
- data/lib/spotify_cli.rb +5 -0
- data/spotify_cli.gemspec +1 -0
- metadata +16 -3
- data/lib/helpers/doc.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0471a10bcc93c07d50e44f8d8cf461d6c608d269
|
4
|
+
data.tar.gz: 4d9001552b8b6eddb50c72929a20f990ecebdba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d02251db2948e80df216d0423e1e3d75301167b6291123579814910e3710f06dac38848304269bb89a213eecdcc09bc5fc39cdb9459c5ac03259e7e6fd035e5a
|
7
|
+
data.tar.gz: 558017104ac1d61029fc01df0b904a9a680028be37176d99f04272203c7b44cd4c59242fe26c9457d9afc8702880d6adacb90f68a8d56594e39791da91274e28
|
data/lib/spotify_cli/api.rb
CHANGED
@@ -1,68 +1,56 @@
|
|
1
1
|
require 'spotify_cli/app'
|
2
|
-
require 'helpers/doc'
|
3
2
|
|
4
3
|
module SpotifyCli
|
5
4
|
class Api
|
6
5
|
PLAY = "▶"
|
7
6
|
STOP = "◼"
|
8
|
-
|
9
7
|
SPOTIFY_SEARCH_API = "https://api.spotify.com/v1/search"
|
10
8
|
|
11
9
|
class << self
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
{{command:spotify next}}
|
17
|
-
EOF
|
10
|
+
# Changes to the next song
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
# - spotify next
|
18
14
|
def next
|
19
15
|
puts "Playing next song"
|
20
16
|
SpotifyCli::App.next!
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
{{command:spotify previous}}
|
28
|
-
EOF
|
19
|
+
# Changes to the previous song
|
20
|
+
#
|
21
|
+
# Usage:
|
22
|
+
# - spotify previous
|
29
23
|
def previous
|
30
24
|
puts "Playing previous song"
|
31
25
|
SpotifyCli::App.prev!
|
32
26
|
end
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
{{command:spotify set_pos 60}}
|
39
|
-
EOF
|
28
|
+
# Sets the position in the song
|
29
|
+
#
|
30
|
+
# Usage:
|
31
|
+
# - spotify set_pos 60
|
40
32
|
def set_pos
|
41
33
|
puts "Setting position to #{ARGV[1]}"
|
42
34
|
SpotifyCli::App.set_pos!(ARGV[1])
|
43
35
|
end
|
44
36
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
{{command:spotify replay}}
|
50
|
-
EOF
|
37
|
+
# Replays the current song
|
38
|
+
#
|
39
|
+
# Usage:
|
40
|
+
# - spotify replay
|
51
41
|
def replay
|
52
42
|
puts "Restarting song"
|
53
43
|
SpotifyCli::App.replay!
|
54
44
|
end
|
55
45
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
{{command:spotify play uri [spotify uri]}}
|
65
|
-
EOF
|
46
|
+
# Play/Pause the current song, or play a specified artist,
|
47
|
+
# track, album, or uri
|
48
|
+
#
|
49
|
+
# Usage:
|
50
|
+
# - spotify play artist [name]
|
51
|
+
# - spotify play track [name]
|
52
|
+
# - spotify play album [name]
|
53
|
+
# - spotify play uri [spotify uri]
|
66
54
|
def play_pause
|
67
55
|
args = ARGV[1..-1]
|
68
56
|
|
@@ -92,25 +80,21 @@ module SpotifyCli
|
|
92
80
|
status
|
93
81
|
end
|
94
82
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
{{command:spotify stop}}
|
101
|
-
EOF
|
83
|
+
# Pause/stop the current song
|
84
|
+
#
|
85
|
+
# Usage:
|
86
|
+
# - spotify pause
|
87
|
+
# - spotify stop
|
102
88
|
def pause
|
103
89
|
SpotifyCli::App.pause!
|
104
90
|
status
|
105
91
|
end
|
106
92
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
{{command:spotify status}}
|
113
|
-
EOF
|
93
|
+
# Show the current song
|
94
|
+
#
|
95
|
+
# Usage:
|
96
|
+
# - spotify
|
97
|
+
# - spotify status
|
114
98
|
def status
|
115
99
|
stat = SpotifyCli::App.status
|
116
100
|
|
@@ -137,26 +121,27 @@ module SpotifyCli
|
|
137
121
|
end
|
138
122
|
end
|
139
123
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
{{command:spotify help}}
|
145
|
-
EOF
|
124
|
+
# Display Help
|
125
|
+
#
|
126
|
+
# Usage:
|
127
|
+
# - spotify help
|
146
128
|
def help(mappings)
|
129
|
+
require 'method_source'
|
130
|
+
|
147
131
|
Dex::UI.frame('Spotify CLI', timing: false) do
|
148
132
|
puts "CLI interface for Spotify"
|
149
133
|
end
|
150
134
|
|
151
135
|
mappings.group_by { |_,v| v }.each do |k, v|
|
152
136
|
v.reject! { |mapping| mapping.first == k.to_s }
|
153
|
-
doc =
|
137
|
+
doc = self.method(k).comment.gsub(/^#\s*/, '')
|
138
|
+
doc = strip_heredoc(doc)
|
154
139
|
|
155
140
|
Dex::UI.frame(k, timing: false) do
|
156
|
-
puts
|
141
|
+
puts strip_heredoc(doc)
|
157
142
|
next if v.empty?
|
158
|
-
puts
|
159
|
-
v.each { |mapping| puts
|
143
|
+
puts "\nAliases:"
|
144
|
+
v.each { |mapping| puts " - #{mapping.first}" }
|
160
145
|
end
|
161
146
|
end
|
162
147
|
end
|
@@ -177,6 +162,20 @@ module SpotifyCli
|
|
177
162
|
|
178
163
|
`#{curl_cmd}`.strip.split("\n")
|
179
164
|
end
|
165
|
+
|
166
|
+
# The following methods is taken from activesupport
|
167
|
+
#
|
168
|
+
# https://github.com/rails/rails/blob/d66e7835bea9505f7003e5038aa19b6ea95ceea1/activesupport/lib/active_support/core_ext/string/strip.rb
|
169
|
+
#
|
170
|
+
# All credit for this method goes to the original authors.
|
171
|
+
# The code is used under the MIT license.
|
172
|
+
#
|
173
|
+
# Strips indentation by removing the amount of leading whitespace in the least indented
|
174
|
+
# non-empty line in the whole string
|
175
|
+
#
|
176
|
+
def strip_heredoc(str)
|
177
|
+
str.gsub(/^#{str.scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
|
178
|
+
end
|
180
179
|
end
|
181
180
|
end
|
182
181
|
end
|
data/lib/spotify_cli/app.rb
CHANGED
@@ -1,101 +1,135 @@
|
|
1
1
|
module SpotifyCli
|
2
2
|
class App
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class << self
|
4
|
+
# Return the state Spotify is in.
|
5
|
+
# Either "playing" or "paused"
|
6
|
+
#
|
7
|
+
# @return state [String]
|
8
|
+
#
|
9
|
+
def state
|
10
|
+
oascript('tell application "Spotify" to player state as string')
|
11
|
+
end
|
6
12
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
13
|
+
# Return a hash representing the current status of Spotify
|
14
|
+
# Contains state, current artist, current album, current track,
|
15
|
+
# duration of the current track, position in the current track,
|
16
|
+
# percent of the track complete
|
17
|
+
#
|
18
|
+
# @return status [Hash]
|
19
|
+
#
|
20
|
+
def status
|
21
|
+
artist = oascript('tell application "Spotify" to artist of current track as string')
|
22
|
+
album = oascript('tell application "Spotify" to album of current track as string')
|
23
|
+
track = oascript('tell application "Spotify" to name of current track as string')
|
24
|
+
duration = oascript(<<-EOF)
|
25
|
+
tell application "Spotify"
|
26
|
+
set durSec to (duration of current track / 1000) as text
|
27
|
+
set tM to (round (durSec / 60) rounding down) as text
|
28
|
+
if length of ((durSec mod 60 div 1) as text) is greater than 1 then
|
29
|
+
set tS to (durSec mod 60 div 1) as text
|
30
|
+
else
|
31
|
+
set tS to ("0" & (durSec mod 60 div 1)) as text
|
32
|
+
end if
|
33
|
+
set myTime to tM as text & ":" & tS as text
|
34
|
+
end tell
|
35
|
+
return myTime
|
36
|
+
EOF
|
37
|
+
position = oascript(<<-EOF)
|
38
|
+
tell application "Spotify"
|
39
|
+
set pos to player position
|
40
|
+
set nM to (round (pos / 60) rounding down) as text
|
41
|
+
if length of ((round (pos mod 60) rounding down) as text) is greater than 1 then
|
42
|
+
set nS to (round (pos mod 60) rounding down) as text
|
43
|
+
else
|
44
|
+
set nS to ("0" & (round (pos mod 60) rounding down)) as text
|
45
|
+
end if
|
46
|
+
set nowAt to nM as text & ":" & nS as text
|
47
|
+
end tell
|
48
|
+
return nowAt
|
49
|
+
EOF
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
51
|
+
{
|
52
|
+
state: state,
|
53
|
+
artist: artist,
|
54
|
+
album: album,
|
55
|
+
track: track,
|
56
|
+
duration: duration,
|
57
|
+
position: position,
|
58
|
+
percent_done: percent_done(position, duration)
|
59
|
+
}
|
60
|
+
end
|
48
61
|
|
49
|
-
|
50
|
-
|
51
|
-
|
62
|
+
# Play or Pause Spotify
|
63
|
+
#
|
64
|
+
def play_pause!
|
65
|
+
oascript('tell application "Spotify" to playpause')
|
66
|
+
end
|
52
67
|
|
53
|
-
|
54
|
-
|
55
|
-
|
68
|
+
# Pause Spotify
|
69
|
+
#
|
70
|
+
def pause!
|
71
|
+
oascript('tell application "Spotify" to pause')
|
72
|
+
end
|
56
73
|
|
57
|
-
|
58
|
-
|
59
|
-
|
74
|
+
# Pause a given URI
|
75
|
+
#
|
76
|
+
# @param uri [String] Spotify URI returned from the API
|
77
|
+
#
|
78
|
+
def play_uri!(uri)
|
79
|
+
oascript("tell application \"Spotify\" to play track \"#{uri}\"")
|
80
|
+
end
|
60
81
|
|
61
|
-
|
62
|
-
|
63
|
-
|
82
|
+
# Change to the next song
|
83
|
+
#
|
84
|
+
def next!
|
85
|
+
oascript('tell application "Spotify" to next track')
|
86
|
+
end
|
64
87
|
|
65
|
-
|
66
|
-
|
67
|
-
|
88
|
+
# Change to the previous song
|
89
|
+
#
|
90
|
+
def previous!
|
91
|
+
oascript(<<-EOF)
|
92
|
+
tell application "Spotify"
|
93
|
+
set player position to 0
|
94
|
+
previous track
|
95
|
+
end tell
|
96
|
+
EOF
|
97
|
+
end
|
68
98
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
99
|
+
# Set position in song to the given point
|
100
|
+
#
|
101
|
+
# @param pos [Int] Position in seconds
|
102
|
+
#
|
103
|
+
def set_pos!(pos)
|
104
|
+
oascript("tell application \"Spotify\" to set player position to #{pos}")
|
105
|
+
end
|
77
106
|
|
78
|
-
|
79
|
-
|
80
|
-
|
107
|
+
# Replay the current song from the beginning
|
108
|
+
#
|
109
|
+
def replay!
|
110
|
+
oascript('tell application "Spotify" to set player position to 0')
|
111
|
+
end
|
81
112
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
113
|
+
private
|
114
|
+
|
115
|
+
def percent_done(position, duration)
|
116
|
+
seconds = ->(parts) do
|
117
|
+
acc = 0
|
118
|
+
multiplier = 1
|
119
|
+
while part = parts.shift
|
120
|
+
acc += part.to_f * multiplier
|
121
|
+
multiplier *= 60
|
122
|
+
end
|
123
|
+
acc
|
89
124
|
end
|
90
|
-
|
125
|
+
pos_parts = position.split(':').reverse
|
126
|
+
dur_parts = duration.split(':').reverse
|
127
|
+
seconds.call(pos_parts) / seconds.call(dur_parts)
|
91
128
|
end
|
92
|
-
pos_parts = position.split(':').reverse
|
93
|
-
dur_parts = duration.split(':').reverse
|
94
|
-
seconds.call(pos_parts) / seconds.call(dur_parts)
|
95
|
-
end
|
96
129
|
|
97
|
-
|
98
|
-
|
130
|
+
def oascript(command)
|
131
|
+
`osascript -e '#{command}'`.strip
|
132
|
+
end
|
99
133
|
end
|
100
134
|
end
|
101
135
|
end
|
data/lib/spotify_cli/version.rb
CHANGED
data/lib/spotify_cli.rb
CHANGED
@@ -3,6 +3,11 @@ require 'dex/ui'
|
|
3
3
|
require 'spotify_cli/api'
|
4
4
|
|
5
5
|
module SpotifyCli
|
6
|
+
# CLI interface for the application
|
7
|
+
# Converts arguments to a mapped command and executes the command
|
8
|
+
#
|
9
|
+
# @param args [Array] CLI arugments
|
10
|
+
#
|
6
11
|
def self.call(args)
|
7
12
|
mappings = {
|
8
13
|
'next' => :next,
|
data/spotify_cli.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = ['spotify']
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
+
spec.add_dependency 'method_source', '~> 0'
|
33
34
|
spec.add_development_dependency "bundler", "~> 1.14"
|
34
35
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
36
|
spec.add_development_dependency "minitest", "~> 5.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spotify_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Nadeau
|
@@ -10,6 +10,20 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2017-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: method_source
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,7 +96,6 @@ files:
|
|
82
96
|
- lib/dex/ui/spinner.rb
|
83
97
|
- lib/dex/ui/stdout_router.rb
|
84
98
|
- lib/dex/ui/terminal.rb
|
85
|
-
- lib/helpers/doc.rb
|
86
99
|
- lib/spotify_cli.rb
|
87
100
|
- lib/spotify_cli/.DS_Store
|
88
101
|
- lib/spotify_cli/api.rb
|
@@ -110,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
123
|
version: '0'
|
111
124
|
requirements: []
|
112
125
|
rubyforge_project:
|
113
|
-
rubygems_version: 2.
|
126
|
+
rubygems_version: 2.6.12
|
114
127
|
signing_key:
|
115
128
|
specification_version: 4
|
116
129
|
summary: Spotify Application wrapper for control via command line
|
data/lib/helpers/doc.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
module MethodAddedHook
|
2
|
-
private
|
3
|
-
|
4
|
-
def method_added(meth)
|
5
|
-
method_added_hook(meth)
|
6
|
-
super
|
7
|
-
end
|
8
|
-
|
9
|
-
def singleton_method_added(meth)
|
10
|
-
method_added_hook(meth)
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def method_added_hook(meth)
|
15
|
-
@@__last_defined_doc__ ||= nil
|
16
|
-
return if !defined?(@@__last_defined_doc__) || @@__last_defined_doc__.nil?
|
17
|
-
@@__class_docs__ ||= {}
|
18
|
-
@@__class_docs__[self.to_s] ||= {}
|
19
|
-
|
20
|
-
@@__class_docs__[self.to_s][meth] = @@__last_defined_doc__
|
21
|
-
@@__last_defined_doc__ = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Module
|
26
|
-
private
|
27
|
-
prepend MethodAddedHook
|
28
|
-
|
29
|
-
def doc(str, meth = nil)
|
30
|
-
return @@__class_docs__[self.to_s][meth] = str if meth
|
31
|
-
@@__last_defined_doc__ = str
|
32
|
-
end
|
33
|
-
|
34
|
-
def defdoc(str, meth, &block)
|
35
|
-
@@__class_docs__[self.to_s][meth] = str
|
36
|
-
define_method(meth, &block)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module Kernel
|
41
|
-
def get_doc(klass, meth)
|
42
|
-
docs = klass.class_variable_get(:@@__class_docs__)
|
43
|
-
docs[self.to_s][meth.to_sym] if docs && docs[self.to_s]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
class String
|
49
|
-
# The following methods is taken from activesupport
|
50
|
-
#
|
51
|
-
# https://github.com/rails/rails/blob/d66e7835bea9505f7003e5038aa19b6ea95ceea1/activesupport/lib/active_support/core_ext/string/strip.rb
|
52
|
-
#
|
53
|
-
# All credit for this method goes to the original authors.
|
54
|
-
# The code is used under the MIT license.
|
55
|
-
#
|
56
|
-
# Strips indentation by removing the amount of leading whitespace in the least indented
|
57
|
-
# non-empty line in the whole string
|
58
|
-
#
|
59
|
-
def strip_heredoc
|
60
|
-
self.gsub(/^#{self.scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
|
61
|
-
end
|
62
|
-
end
|