shellify 1.1.2 → 1.3.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 +4 -4
- data/lib/shellify/cli.rb +66 -37
- data/lib/shellify/config.rb +5 -5
- data/lib/shellify/oauth_callback_handler.rb +4 -4
- data/lib/shellify/rspotify_patch.rb +25 -0
- data/lib/shellify/user.rb +5 -5
- data/lib/shellify/utils.rb +1 -1
- data/lib/shellify/version.rb +1 -1
- data/lib/shellify.rb +1 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f62fa09eb7f90e2bc486320a18dc252ffb8a751a6d67e72ce3fb5a149e1965ef
|
4
|
+
data.tar.gz: b9cb6abde7095e6b5f2e358e6962897dbb797b3e90823bfb8c3422b1bacf7766
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd101fc048837d34a1db93c0c36c8ebb3270ea2779628f154309d58837f4d0666ba673b53c954d4b6ca06ee7efdf30fa214f8e561c70a391d1dfc35dba0926fc
|
7
|
+
data.tar.gz: c2fa18b6cebc2eff9ca4406576ba24efb98d77fbfd015ee9d1312b84ac98a4dd7ce0ce0d18f6dd7cf458f8ee32d92514a23734b3ec618d84bc2de83a9533ab9d
|
data/lib/shellify/cli.rb
CHANGED
@@ -22,8 +22,8 @@ module Shellify
|
|
22
22
|
command :configure do |c|
|
23
23
|
c.description = 'Set the Spotify client_id and client_secret'
|
24
24
|
c.action do
|
25
|
-
client_id = ask(
|
26
|
-
client_secret = ask(
|
25
|
+
client_id = ask('Spotify Client ID: ')
|
26
|
+
client_secret = ask('Spotify Client Secret: ') { |q| q.echo = '*' }
|
27
27
|
@config.client_id = client_id
|
28
28
|
@config.client_secret = client_secret
|
29
29
|
@config.save!
|
@@ -58,15 +58,15 @@ module Shellify
|
|
58
58
|
command :playing do |c|
|
59
59
|
c.description = 'List information about the current song'
|
60
60
|
c.action do
|
61
|
-
return puts
|
61
|
+
return puts ' Nothing playing' unless @user.player.playing?
|
62
62
|
|
63
|
-
|
63
|
+
print_currently_playing
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
67
|
command :volume do |c|
|
68
68
|
c.description = 'Set the volume of the current playback device'
|
69
|
-
c.action do |args,
|
69
|
+
c.action do |args, _options|
|
70
70
|
@user.player.volume(args[0])
|
71
71
|
end
|
72
72
|
end
|
@@ -91,52 +91,73 @@ module Shellify
|
|
91
91
|
c.description = 'List your playlists'
|
92
92
|
c.action do
|
93
93
|
@user.playlists.each do |playlist|
|
94
|
-
puts " #{playlist.name} - #{playlist.owner.display_name}#{
|
94
|
+
puts " #{playlist.name} - #{playlist.owner.display_name}#{' - Collaborative' if playlist.collaborative}"
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
99
|
command :add do |c|
|
100
|
-
c.description = 'Add the current song to the provided playlist'
|
100
|
+
c.description = 'Add the current song or album to the provided playlist'
|
101
|
+
c.option '-a', '--album'
|
101
102
|
c.action do |args, options|
|
102
|
-
return puts
|
103
|
+
return puts ' Nothing playing' unless @user.player.playing?
|
103
104
|
|
104
105
|
exit_with_message(local_track_message, 0) if track_is_local?(playing)
|
105
106
|
playlist = @user.playlists.find { |p| p.name == args[0] }
|
106
|
-
return puts
|
107
|
-
|
107
|
+
return puts ' Playlist not found' unless playlist
|
108
|
+
|
109
|
+
exit_with_message(add_to_collaborative_playlist_message, 0) if playlist.owner.id != @user.id
|
110
|
+
|
111
|
+
item = options.album ? playing.album.tracks : [playing]
|
112
|
+
playlist.add_tracks!(item)
|
113
|
+
end
|
114
|
+
end
|
108
115
|
|
109
|
-
|
116
|
+
command :queue do |c|
|
117
|
+
c.description = 'List the next songs in the queue'
|
118
|
+
c.action do
|
119
|
+
items = @user.player.next_up
|
120
|
+
exit_with_message(' Nothing in the queue', 0) if items.empty?
|
121
|
+
items.each.with_index(1) do |item, i|
|
122
|
+
case item.type
|
123
|
+
when 'episode'
|
124
|
+
puts " #{i.to_s.rjust(items.size.to_s.size, ' ')} - #{item.name} - #{item.show.name}"
|
125
|
+
when 'track'
|
126
|
+
puts " #{i.to_s.rjust(items.size.to_s.size, ' ')} - #{item.name} - "\
|
127
|
+
"#{item.artists.map(&:name).join(', ')}"
|
128
|
+
end
|
129
|
+
end
|
110
130
|
end
|
111
131
|
end
|
112
132
|
|
113
133
|
command :remove do |c|
|
114
|
-
c.description = 'Remove the currently playing song from the provided playlist'
|
134
|
+
c.description = 'Remove the currently playing song or album from the provided playlist'
|
135
|
+
c.option '-a', '--album'
|
115
136
|
c.action do |args, options|
|
116
|
-
return puts
|
137
|
+
return puts ' Nothing playing' unless @user.player.playing?
|
117
138
|
|
118
139
|
exit_with_message(local_track_message, 0) if track_is_local?(playing)
|
119
140
|
playlist = @user.playlists.find { |p| p.name == args[0] }
|
120
|
-
return puts
|
121
|
-
|
141
|
+
return puts ' Playlist not found' unless playlist
|
142
|
+
|
143
|
+
exit_with_message(add_to_collaborative_playlist_message, 0) if playlist.owner.id != @user.id
|
122
144
|
|
123
|
-
|
145
|
+
item = options.album ? playing.album.tracks : [playing]
|
146
|
+
playlist.remove_tracks!(item)
|
124
147
|
end
|
125
148
|
end
|
126
149
|
|
127
150
|
command :play do |c|
|
128
151
|
c.description = 'Play or Pause on the currently playing device'
|
129
152
|
c.action do
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
print_current_song
|
136
|
-
end
|
137
|
-
rescue RestClient::NotFound
|
138
|
-
@user.player.play(@user.devices.first.id)
|
153
|
+
if @user.player.playing?
|
154
|
+
@user.player.pause
|
155
|
+
else
|
156
|
+
@user.player.play
|
157
|
+
print_currently_playing
|
139
158
|
end
|
159
|
+
rescue RestClient::NotFound
|
160
|
+
@user.player.play(@user.devices.first.id)
|
140
161
|
end
|
141
162
|
end
|
142
163
|
|
@@ -144,7 +165,7 @@ module Shellify
|
|
144
165
|
c.description = 'Skip to the next song in the queue'
|
145
166
|
c.action do
|
146
167
|
@user.player.next
|
147
|
-
|
168
|
+
print_currently_playing
|
148
169
|
end
|
149
170
|
end
|
150
171
|
|
@@ -152,7 +173,7 @@ module Shellify
|
|
152
173
|
c.description = 'Skip the the previous song in the queue'
|
153
174
|
c.action do
|
154
175
|
@user.player.previous
|
155
|
-
|
176
|
+
print_currently_playing
|
156
177
|
end
|
157
178
|
end
|
158
179
|
|
@@ -160,15 +181,15 @@ module Shellify
|
|
160
181
|
c.description = 'Restart the currently playing song'
|
161
182
|
c.action do
|
162
183
|
@user.player.seek 0
|
163
|
-
|
184
|
+
print_currently_playing
|
164
185
|
end
|
165
186
|
end
|
166
187
|
|
167
188
|
command :seek do |c|
|
168
189
|
c.description = 'Seek to the specified time in the current song'
|
169
|
-
c.action do |args,
|
190
|
+
c.action do |args, _option|
|
170
191
|
@user.player.seek(time_to_ms(args[0]))
|
171
|
-
|
192
|
+
print_currently_playing
|
172
193
|
end
|
173
194
|
end
|
174
195
|
|
@@ -191,20 +212,28 @@ module Shellify
|
|
191
212
|
end
|
192
213
|
|
193
214
|
def add_to_collaborative_playlist_message
|
194
|
-
"
|
215
|
+
" Shellify can't perform this action for collaborative playlists you don't own"
|
195
216
|
end
|
196
217
|
|
197
218
|
def track_is_local?(track)
|
198
219
|
track.uri.split(':')[1] == 'local'
|
199
220
|
end
|
200
221
|
|
201
|
-
def
|
202
|
-
puts
|
203
|
-
|
204
|
-
"#{
|
205
|
-
"#{
|
206
|
-
"#{
|
222
|
+
def current_song
|
223
|
+
puts "Now Playing - #{duration_to_s(@user.player.progress)}/#{duration_to_s(playing.duration_ms)}"\
|
224
|
+
"#{' - ♥' if !track_is_local?(playing) && @user.saved_tracks?([playing]).first}"\
|
225
|
+
"#{' - local' if track_is_local?(playing)}\n"\
|
226
|
+
" #{playing.name}\n"\
|
227
|
+
" #{playing.album.name}\n"\
|
228
|
+
" #{playing.artists.map(&:name).join(', ')}"
|
229
|
+
end
|
207
230
|
|
231
|
+
def print_currently_playing
|
232
|
+
if playing.nil?
|
233
|
+
puts "Now Playing - Podcast - #{duration_to_s(@user.player.progress)}"
|
234
|
+
else
|
235
|
+
current_song
|
236
|
+
end
|
208
237
|
end
|
209
238
|
|
210
239
|
def exit_with_message(message, code = 1)
|
data/lib/shellify/config.rb
CHANGED
@@ -4,8 +4,8 @@ module Shellify
|
|
4
4
|
class Config
|
5
5
|
attr_accessor :client_id, :client_secret, :config_dir
|
6
6
|
|
7
|
-
CONFIG_DIR = ENV['HOME']
|
8
|
-
CONFIG_FILE = CONFIG_DIR
|
7
|
+
CONFIG_DIR = "#{ENV['HOME']}/.config/shellify"
|
8
|
+
CONFIG_FILE = "#{CONFIG_DIR}/config.json"
|
9
9
|
SPOTIFY_AUTHORIZATION_SCOPES = %w[
|
10
10
|
user-read-playback-state
|
11
11
|
user-modify-playback-state
|
@@ -31,16 +31,16 @@ module Shellify
|
|
31
31
|
|
32
32
|
def save!
|
33
33
|
File.open(CONFIG_FILE, 'w') do |file|
|
34
|
-
file.write(JSON.pretty_generate({client_id: @client_id, client_secret: @client_secret}))
|
34
|
+
file.write(JSON.pretty_generate({ client_id: @client_id, client_secret: @client_secret }))
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def load_config
|
41
|
-
return unless File.
|
41
|
+
return unless File.exist?(CONFIG_FILE)
|
42
42
|
|
43
|
-
JSON.parse(File.read(CONFIG_FILE)).each_pair { |k,v| instance_variable_set("@#{k}", v) }
|
43
|
+
JSON.parse(File.read(CONFIG_FILE)).each_pair { |k, v| instance_variable_set("@#{k}", v) }
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -25,7 +25,7 @@ module Shellify
|
|
25
25
|
begin
|
26
26
|
tokens = fetch_tokens(params['code'])
|
27
27
|
rescue RestClient::Exception => e
|
28
|
-
body = "Spotify didn't like that\n
|
28
|
+
body = "Spotify didn't like that\n#{e.response}"
|
29
29
|
end
|
30
30
|
|
31
31
|
@client.puts headers(body.length)
|
@@ -41,7 +41,7 @@ module Shellify
|
|
41
41
|
def headers(content_length)
|
42
42
|
[
|
43
43
|
'HTTP/1.1 200 Ok',
|
44
|
-
"date: #{Time.now.utc.strftime(
|
44
|
+
"date: #{Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')}",
|
45
45
|
'server: ruby',
|
46
46
|
"Content-Length: #{content_length}",
|
47
47
|
'',
|
@@ -51,7 +51,7 @@ module Shellify
|
|
51
51
|
|
52
52
|
def fetch_tokens(code)
|
53
53
|
headers = {
|
54
|
-
'Authorization':
|
54
|
+
'Authorization': 'Basic ' + Base64.strict_encode64("#{@config.client_id}:#{@config.client_secret}"),
|
55
55
|
}
|
56
56
|
|
57
57
|
params = {
|
@@ -62,7 +62,7 @@ module Shellify
|
|
62
62
|
code: code,
|
63
63
|
}
|
64
64
|
|
65
|
-
JSON.parse(RestClient.post(
|
65
|
+
JSON.parse(RestClient.post('https://accounts.spotify.com/api/token', params, headers))
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpotify
|
4
|
+
class Player
|
5
|
+
def next_up
|
6
|
+
url = 'me/player/queue'
|
7
|
+
response = User.oauth_get(@user.id, url)
|
8
|
+
return response if RSpotify.raw_response
|
9
|
+
|
10
|
+
response['queue'].map do |item|
|
11
|
+
type_class = RSpotify.const_get(item['type'].capitalize)
|
12
|
+
type_class.new item
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def currently_playing
|
17
|
+
url = 'me/player/currently-playing'
|
18
|
+
response = User.oauth_get(@user.id, url)
|
19
|
+
return response if RSpotify.raw_response
|
20
|
+
|
21
|
+
type_class = RSpotify.const_get(response['currently_playing_type'].capitalize)
|
22
|
+
type_class.new response['item'] unless response['item'].nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/shellify/user.rb
CHANGED
@@ -28,25 +28,25 @@ module Shellify
|
|
28
28
|
|
29
29
|
def save!
|
30
30
|
File.open(@user_file_path, 'w') do |file|
|
31
|
-
file.write(JSON.pretty_generate({id: @id, token: @token, refresh_token: @refresh_token}))
|
31
|
+
file.write(JSON.pretty_generate({ id: @id, token: @token, refresh_token: @refresh_token }))
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
37
|
def load_persisted_user
|
38
|
-
JSON.parse(File.read(@user_file_path)).each_pair { |k,v| instance_variable_set("@#{k}", v) }
|
38
|
+
JSON.parse(File.read(@user_file_path)).each_pair { |k, v| instance_variable_set("@#{k}", v) }
|
39
39
|
end
|
40
40
|
|
41
41
|
def access_refresh_callback
|
42
|
-
|
42
|
+
proc do |new_access_token, _token_lifetime|
|
43
43
|
@token = new_access_token
|
44
44
|
save!
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
def create_user_file
|
49
|
-
return if File.
|
49
|
+
return if File.exist?(@user_file_path)
|
50
50
|
|
51
51
|
FileUtils.mkdir_p(@config_dir)
|
52
52
|
FileUtils.touch(@user_file_path)
|
@@ -56,7 +56,7 @@ module Shellify
|
|
56
56
|
return unless File.zero?(@user_file_path)
|
57
57
|
|
58
58
|
File.open(@user_file_path, 'w') do |file|
|
59
|
-
file.write(JSON.pretty_generate({id: '', token: '', refresh_token: ''
|
59
|
+
file.write(JSON.pretty_generate({ id: '', token: '', refresh_token: '' }))
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
data/lib/shellify/utils.rb
CHANGED
data/lib/shellify/version.rb
CHANGED
data/lib/shellify.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shellify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Povah
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- lib/shellify/cli.rb
|
82
82
|
- lib/shellify/config.rb
|
83
83
|
- lib/shellify/oauth_callback_handler.rb
|
84
|
+
- lib/shellify/rspotify_patch.rb
|
84
85
|
- lib/shellify/user.rb
|
85
86
|
- lib/shellify/utils.rb
|
86
87
|
- lib/shellify/version.rb
|
@@ -99,14 +100,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
100
|
requirements:
|
100
101
|
- - ">="
|
101
102
|
- !ruby/object:Gem::Version
|
102
|
-
version: 2.
|
103
|
+
version: 2.7.0
|
103
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
105
|
requirements:
|
105
106
|
- - ">="
|
106
107
|
- !ruby/object:Gem::Version
|
107
108
|
version: '0'
|
108
109
|
requirements: []
|
109
|
-
rubygems_version: 3.
|
110
|
+
rubygems_version: 3.4.17
|
110
111
|
signing_key:
|
111
112
|
specification_version: 4
|
112
113
|
summary: Use Spotify from the command line
|