spotify_cli 0.1.0
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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/README.md +101 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/spotify +5 -0
- data/lib/dex/ui/README.md +20 -0
- data/lib/dex/ui/ansi.rb +48 -0
- data/lib/dex/ui/box.rb +15 -0
- data/lib/dex/ui/color.rb +57 -0
- data/lib/dex/ui/formatter.rb +155 -0
- data/lib/dex/ui/frame.rb +166 -0
- data/lib/dex/ui/glyph.rb +49 -0
- data/lib/dex/ui/progress.rb +19 -0
- data/lib/dex/ui/prompt.rb +121 -0
- data/lib/dex/ui/spinner.rb +168 -0
- data/lib/dex/ui/stdout_router.rb +186 -0
- data/lib/dex/ui/terminal.rb +18 -0
- data/lib/dex/ui.rb +83 -0
- data/lib/helpers/doc.rb +62 -0
- data/lib/spotify_cli/.DS_Store +0 -0
- data/lib/spotify_cli/api.rb +182 -0
- data/lib/spotify_cli/app.rb +101 -0
- data/lib/spotify_cli/version.rb +3 -0
- data/lib/spotify_cli.rb +38 -0
- data/spotify_cli.gemspec +36 -0
- metadata +118 -0
data/lib/dex/ui.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Dex
|
2
|
+
module UI
|
3
|
+
autoload :ANSI, 'dex/ui/ansi'
|
4
|
+
autoload :Glyph, 'dex/ui/glyph'
|
5
|
+
autoload :Color, 'dex/ui/color'
|
6
|
+
autoload :Box, 'dex/ui/box'
|
7
|
+
autoload :Frame, 'dex/ui/frame'
|
8
|
+
autoload :Progress, 'dex/ui/progress'
|
9
|
+
autoload :Prompt, 'dex/ui/prompt'
|
10
|
+
autoload :Terminal, 'dex/ui/terminal'
|
11
|
+
autoload :Formatter, 'dex/ui/formatter'
|
12
|
+
autoload :Spinner, 'dex/ui/spinner'
|
13
|
+
|
14
|
+
# TODO: this, better
|
15
|
+
SpinGroup = Spinner::SpinGroup
|
16
|
+
|
17
|
+
# TODO: test
|
18
|
+
def self.glyph(handle)
|
19
|
+
Dex::UI::Glyph.lookup(handle)
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: test
|
23
|
+
def self.resolve_color(input)
|
24
|
+
case input
|
25
|
+
when Symbol
|
26
|
+
Dex::UI::Color.lookup(input)
|
27
|
+
else
|
28
|
+
input
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.confirm(question)
|
33
|
+
Dex::UI::Prompt.confirm(question)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.ask(question, **kwargs)
|
37
|
+
Dex::UI::Prompt.ask(question, **kwargs)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.resolve_text(input)
|
41
|
+
return input if input.nil?
|
42
|
+
Dex::UI::Formatter.new(input).format
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.fmt(input, enable_color: true)
|
46
|
+
Dex::UI::Formatter.new(input).format(enable_color: enable_color)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.frame(*args, &block)
|
50
|
+
Dex::UI::Frame.open(*args, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.spinner(*args, &block)
|
54
|
+
Dex::UI::Spinner.spin(*args, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.with_frame_color(color, &block)
|
58
|
+
Dex::UI::Frame.with_frame_color_override(color, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.log_output_to(path)
|
62
|
+
if Dex::UI::StdoutRouter.duplicate_output_to
|
63
|
+
raise "multiple logs not allowed"
|
64
|
+
end
|
65
|
+
Dex::UI::StdoutRouter.duplicate_output_to = File.open(path, 'w')
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
f = Dex::UI::StdoutRouter.duplicate_output_to
|
69
|
+
f.close
|
70
|
+
Dex::UI::StdoutRouter.duplicate_output_to = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.raw
|
74
|
+
prev = Thread.current[:no_dexui_frame_inset]
|
75
|
+
Thread.current[:no_dexui_frame_inset] = true
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
Thread.current[:no_dexui_frame_inset] = prev
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
require 'dex/ui/stdout_router'
|
data/lib/helpers/doc.rb
ADDED
@@ -0,0 +1,62 @@
|
|
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
|
Binary file
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spotify_cli/app'
|
2
|
+
require 'helpers/doc'
|
3
|
+
|
4
|
+
module SpotifyCli
|
5
|
+
class Api
|
6
|
+
PLAY = "▶"
|
7
|
+
STOP = "◼"
|
8
|
+
|
9
|
+
SPOTIFY_SEARCH_API = "https://api.spotify.com/v1/search"
|
10
|
+
|
11
|
+
class << self
|
12
|
+
doc <<-EOF
|
13
|
+
Changes to the next song
|
14
|
+
|
15
|
+
{{bold:Usage:}}
|
16
|
+
{{command:spotify next}}
|
17
|
+
EOF
|
18
|
+
def next
|
19
|
+
puts "Playing next song"
|
20
|
+
SpotifyCli::App.next!
|
21
|
+
end
|
22
|
+
|
23
|
+
doc <<-EOF
|
24
|
+
Changes to the previous song
|
25
|
+
|
26
|
+
{{bold:Usage:}}
|
27
|
+
{{command:spotify previous}}
|
28
|
+
EOF
|
29
|
+
def previous
|
30
|
+
puts "Playing previous song"
|
31
|
+
SpotifyCli::App.prev!
|
32
|
+
end
|
33
|
+
|
34
|
+
doc <<-EOF
|
35
|
+
Sets the position in the song
|
36
|
+
|
37
|
+
{{bold:Usage:}}
|
38
|
+
{{command:spotify set_pos 60}}
|
39
|
+
EOF
|
40
|
+
def set_pos
|
41
|
+
puts "Setting position to #{ARGV[1]}"
|
42
|
+
SpotifyCli::App.set_pos!(ARGV[1])
|
43
|
+
end
|
44
|
+
|
45
|
+
doc <<-EOF
|
46
|
+
Replays the current song
|
47
|
+
|
48
|
+
{{bold:Usage:}}
|
49
|
+
{{command:spotify replay}}
|
50
|
+
EOF
|
51
|
+
def replay
|
52
|
+
puts "Restarting song"
|
53
|
+
SpotifyCli::App.replay!
|
54
|
+
end
|
55
|
+
|
56
|
+
doc <<-EOF
|
57
|
+
Play/Pause the current song, or play a specified artist,
|
58
|
+
track, album, or uri
|
59
|
+
|
60
|
+
{{bold:Usage:}}
|
61
|
+
{{command:spotify play artist [name]}}
|
62
|
+
{{command:spotify play track [name]}}
|
63
|
+
{{command:spotify play album [name]}}
|
64
|
+
{{command:spotify play uri [spotify uri]}}
|
65
|
+
EOF
|
66
|
+
def play_pause
|
67
|
+
args = ARGV[1..-1]
|
68
|
+
|
69
|
+
if args.empty?
|
70
|
+
# no specifying paremeter, this is a standard play/pause
|
71
|
+
SpotifyCli::App.play_pause!
|
72
|
+
status
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
arg = args.shift
|
77
|
+
type = arg == 'song' ? 'track' : arg
|
78
|
+
|
79
|
+
Dex::UI.frame("Searching for #{type}", timing: false) do
|
80
|
+
play_uri = case type
|
81
|
+
when 'album', 'artist', 'track'
|
82
|
+
results = search_and_play(type: type, query: args.join(' '))
|
83
|
+
results.first
|
84
|
+
when 'uri'
|
85
|
+
args.first
|
86
|
+
end
|
87
|
+
puts "Results found, playing"
|
88
|
+
SpotifyCli::App.play_uri!(play_uri)
|
89
|
+
sleep 0.05 # Give time for the app to switch otherwise status may be stale
|
90
|
+
end
|
91
|
+
|
92
|
+
status
|
93
|
+
end
|
94
|
+
|
95
|
+
doc <<-EOF
|
96
|
+
Pause/stop the current song
|
97
|
+
|
98
|
+
{{bold:Usage:}}
|
99
|
+
{{command:spotify pause}}
|
100
|
+
{{command:spotify stop}}
|
101
|
+
EOF
|
102
|
+
def pause
|
103
|
+
SpotifyCli::App.pause!
|
104
|
+
status
|
105
|
+
end
|
106
|
+
|
107
|
+
doc <<-EOF
|
108
|
+
Show the current song
|
109
|
+
|
110
|
+
{{bold:Usage:}}
|
111
|
+
{{command:spotify status}}
|
112
|
+
EOF
|
113
|
+
def status
|
114
|
+
stat = SpotifyCli::App.status
|
115
|
+
|
116
|
+
time = "#{stat[:position]} / #{stat[:duration]}"
|
117
|
+
state_sym = case stat[:state]
|
118
|
+
when 'playing'
|
119
|
+
PLAY
|
120
|
+
else
|
121
|
+
STOP
|
122
|
+
end
|
123
|
+
# 3 for padding around time, and symbol, and space for the symbol, 2 for frame
|
124
|
+
width = Dex::UI::Terminal.width - time.size - 5
|
125
|
+
|
126
|
+
Dex::UI.frame(stat[:track], timing: false) do
|
127
|
+
puts Dex::UI.resolve_text([
|
128
|
+
"{{bold:Artist:}} #{stat[:artist]}",
|
129
|
+
"{{bold:Album:}} #{stat[:album]}",
|
130
|
+
].join("\n"))
|
131
|
+
puts [
|
132
|
+
Dex::UI::Progress.progress(stat[:percent_done], width),
|
133
|
+
state_sym,
|
134
|
+
time
|
135
|
+
].join(' ')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
doc <<-EOF
|
140
|
+
Display Help
|
141
|
+
|
142
|
+
{{bold:Usage:}}
|
143
|
+
{{command:spotify}}
|
144
|
+
{{command:spotify help}}
|
145
|
+
EOF
|
146
|
+
def help(mappings)
|
147
|
+
Dex::UI.frame('Spotify CLI', timing: false) do
|
148
|
+
puts "CLI interface for Spotify"
|
149
|
+
end
|
150
|
+
|
151
|
+
mappings.group_by { |_,v| v }.each do |k, v|
|
152
|
+
v.reject! { |mapping| mapping.first == k.to_s }
|
153
|
+
doc = get_doc(self.class, k.to_s).strip_heredoc
|
154
|
+
|
155
|
+
Dex::UI.frame(k, timing: false) do
|
156
|
+
puts puts Dex::UI.resolve_text(doc)
|
157
|
+
next if v.empty?
|
158
|
+
puts Dex::UI.resolve_text("{{bold:Aliases:}}")
|
159
|
+
v.each { |mapping| puts Dex::UI.resolve_text(" - {{info:#{mapping.first}}}") }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def search_and_play(args)
|
167
|
+
type = args[:type]
|
168
|
+
type2 = args[:type2] || type
|
169
|
+
query = args[:query]
|
170
|
+
limit = args[:limit] || 1
|
171
|
+
puts "Searching #{type}s for: #{query}";
|
172
|
+
|
173
|
+
curl_cmd = <<-EOF
|
174
|
+
curl -s -G #{SPOTIFY_SEARCH_API} --data-urlencode "q=#{query}" -d "type=#{type}&limit=#{limit}&offset=0" -H "Accept: application/json" \
|
175
|
+
| grep -E -o "spotify:#{type2}:[a-zA-Z0-9]+" -m #{limit}
|
176
|
+
EOF
|
177
|
+
|
178
|
+
`#{curl_cmd}`.strip.split("\n")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module SpotifyCli
|
2
|
+
class App
|
3
|
+
def self.state
|
4
|
+
oascript('tell application "Spotify" to player state as string')
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.status
|
8
|
+
artist = oascript('tell application "Spotify" to artist of current track as string')
|
9
|
+
album = oascript('tell application "Spotify" to album of current track as string')
|
10
|
+
track = oascript('tell application "Spotify" to name of current track as string')
|
11
|
+
duration = oascript(<<-EOF)
|
12
|
+
tell application "Spotify"
|
13
|
+
set durSec to (duration of current track / 1000) as text
|
14
|
+
set tM to (round (durSec / 60) rounding down) as text
|
15
|
+
if length of ((durSec mod 60 div 1) as text) is greater than 1 then
|
16
|
+
set tS to (durSec mod 60 div 1) as text
|
17
|
+
else
|
18
|
+
set tS to ("0" & (durSec mod 60 div 1)) as text
|
19
|
+
end if
|
20
|
+
set myTime to tM as text & ":" & tS as text
|
21
|
+
end tell
|
22
|
+
return myTime
|
23
|
+
EOF
|
24
|
+
position = oascript(<<-EOF)
|
25
|
+
tell application "Spotify"
|
26
|
+
set pos to player position
|
27
|
+
set nM to (round (pos / 60) rounding down) as text
|
28
|
+
if length of ((round (pos mod 60) rounding down) as text) is greater than 1 then
|
29
|
+
set nS to (round (pos mod 60) rounding down) as text
|
30
|
+
else
|
31
|
+
set nS to ("0" & (round (pos mod 60) rounding down)) as text
|
32
|
+
end if
|
33
|
+
set nowAt to nM as text & ":" & nS as text
|
34
|
+
end tell
|
35
|
+
return nowAt
|
36
|
+
EOF
|
37
|
+
|
38
|
+
{
|
39
|
+
state: state,
|
40
|
+
artist: artist,
|
41
|
+
album: album,
|
42
|
+
track: track,
|
43
|
+
duration: duration,
|
44
|
+
position: position,
|
45
|
+
percent_done: percent_done(position, duration)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.play_pause!
|
50
|
+
oascript('tell application "Spotify" to playpause')
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.pause!
|
54
|
+
oascript('tell application "Spotify" to pause')
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.play_uri!(uri)
|
58
|
+
oascript("tell application \"Spotify\" to play track \"#{uri}\"")
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.next!
|
62
|
+
oascript('tell application "Spotify" to next track')
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.set_pos!(pos)
|
66
|
+
oascript("tell application \"Spotify\" to set player position to #{pos}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.previous!
|
70
|
+
oascript(<<-EOF)
|
71
|
+
tell application "Spotify"
|
72
|
+
set player position to 0
|
73
|
+
previous track
|
74
|
+
end tell
|
75
|
+
EOF
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.replay!
|
79
|
+
oascript('tell application "Spotify" to set player position to 0')
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.percent_done(position, duration)
|
83
|
+
seconds = ->(parts) do
|
84
|
+
acc = 0
|
85
|
+
multiplier = 1
|
86
|
+
while part = parts.shift
|
87
|
+
acc += part.to_f * multiplier
|
88
|
+
multiplier *= 60
|
89
|
+
end
|
90
|
+
acc
|
91
|
+
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
|
+
|
97
|
+
def self.oascript(command)
|
98
|
+
`osascript -e '#{command}'`.strip
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/spotify_cli.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spotify_cli/version'
|
2
|
+
require 'dex/ui'
|
3
|
+
require 'spotify_cli/api'
|
4
|
+
|
5
|
+
module SpotifyCli
|
6
|
+
def self.call(args)
|
7
|
+
mappings = {
|
8
|
+
'next' => :next,
|
9
|
+
'n' => :next,
|
10
|
+
'previous' => :previous,
|
11
|
+
'pr' => :previous,
|
12
|
+
'set_pos' => :set_pos,
|
13
|
+
'pos' => :set_pos,
|
14
|
+
'replay' => :replay,
|
15
|
+
'rep' => :replay,
|
16
|
+
'restart' => :replay,
|
17
|
+
'pause' => :pause,
|
18
|
+
'stop' => :pause,
|
19
|
+
'play' => :play_pause,
|
20
|
+
'p' => :play_pause,
|
21
|
+
'play_pause' => :play_pause,
|
22
|
+
'status' => :status,
|
23
|
+
's' => :status,
|
24
|
+
'help' => :help
|
25
|
+
}
|
26
|
+
|
27
|
+
if args.empty?
|
28
|
+
SpotifyCli::Api.status
|
29
|
+
else
|
30
|
+
mapping = mappings[args.first]
|
31
|
+
if mapping.nil? || mapping == :help
|
32
|
+
SpotifyCli::Api.help(mappings)
|
33
|
+
else
|
34
|
+
SpotifyCli::Api.send(mapping)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spotify_cli.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'spotify_cli/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "spotify_cli"
|
8
|
+
spec.version = SpotifyCli::VERSION
|
9
|
+
spec.authors = ["Julian Nadeau"]
|
10
|
+
spec.email = ["julian@jnadeau.ca"]
|
11
|
+
spec.license = "MIT"
|
12
|
+
|
13
|
+
spec.summary = "Spotify Application wrapper for control via command line"
|
14
|
+
spec.description = "Allow control of Spotify using a pretty UI interface. Intentionally simple."
|
15
|
+
spec.homepage = "https://github.com/jules2689/spotify_cli"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
"public gem pushes."
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "bin"
|
30
|
+
spec.executables = ['spotify']
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
34
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spotify_cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julian Nadeau
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
description: Allow control of Spotify using a pretty UI interface. Intentionally simple.
|
56
|
+
email:
|
57
|
+
- julian@jnadeau.ca
|
58
|
+
executables:
|
59
|
+
- spotify
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".DS_Store"
|
64
|
+
- ".gitignore"
|
65
|
+
- ".travis.yml"
|
66
|
+
- CODE_OF_CONDUCT.md
|
67
|
+
- Gemfile
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/console
|
71
|
+
- bin/setup
|
72
|
+
- bin/spotify
|
73
|
+
- lib/dex/ui.rb
|
74
|
+
- lib/dex/ui/README.md
|
75
|
+
- lib/dex/ui/ansi.rb
|
76
|
+
- lib/dex/ui/box.rb
|
77
|
+
- lib/dex/ui/color.rb
|
78
|
+
- lib/dex/ui/formatter.rb
|
79
|
+
- lib/dex/ui/frame.rb
|
80
|
+
- lib/dex/ui/glyph.rb
|
81
|
+
- lib/dex/ui/progress.rb
|
82
|
+
- lib/dex/ui/prompt.rb
|
83
|
+
- lib/dex/ui/spinner.rb
|
84
|
+
- lib/dex/ui/stdout_router.rb
|
85
|
+
- lib/dex/ui/terminal.rb
|
86
|
+
- lib/helpers/doc.rb
|
87
|
+
- lib/spotify_cli.rb
|
88
|
+
- lib/spotify_cli/.DS_Store
|
89
|
+
- lib/spotify_cli/api.rb
|
90
|
+
- lib/spotify_cli/app.rb
|
91
|
+
- lib/spotify_cli/version.rb
|
92
|
+
- spotify_cli.gemspec
|
93
|
+
homepage: https://github.com/jules2689/spotify_cli
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata:
|
97
|
+
allowed_push_host: https://rubygems.org
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.5.1
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Spotify Application wrapper for control via command line
|
118
|
+
test_files: []
|