shellify 1.1.1 → 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 +69 -34
- 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,50 +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
|
+
return puts ' Playlist not found' unless playlist
|
108
|
+
|
109
|
+
exit_with_message(add_to_collaborative_playlist_message, 0) if playlist.owner.id != @user.id
|
107
110
|
|
108
|
-
|
111
|
+
item = options.album ? playing.album.tracks : [playing]
|
112
|
+
playlist.add_tracks!(item)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
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
|
109
130
|
end
|
110
131
|
end
|
111
132
|
|
112
133
|
command :remove do |c|
|
113
|
-
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'
|
114
136
|
c.action do |args, options|
|
115
|
-
return puts
|
137
|
+
return puts ' Nothing playing' unless @user.player.playing?
|
116
138
|
|
117
139
|
exit_with_message(local_track_message, 0) if track_is_local?(playing)
|
118
140
|
playlist = @user.playlists.find { |p| p.name == args[0] }
|
119
|
-
return puts
|
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
|
120
144
|
|
121
|
-
|
145
|
+
item = options.album ? playing.album.tracks : [playing]
|
146
|
+
playlist.remove_tracks!(item)
|
122
147
|
end
|
123
148
|
end
|
124
149
|
|
125
150
|
command :play do |c|
|
126
151
|
c.description = 'Play or Pause on the currently playing device'
|
127
152
|
c.action do
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
print_current_song
|
134
|
-
end
|
135
|
-
rescue RestClient::NotFound
|
136
|
-
@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
|
137
158
|
end
|
159
|
+
rescue RestClient::NotFound
|
160
|
+
@user.player.play(@user.devices.first.id)
|
138
161
|
end
|
139
162
|
end
|
140
163
|
|
@@ -142,7 +165,7 @@ module Shellify
|
|
142
165
|
c.description = 'Skip to the next song in the queue'
|
143
166
|
c.action do
|
144
167
|
@user.player.next
|
145
|
-
|
168
|
+
print_currently_playing
|
146
169
|
end
|
147
170
|
end
|
148
171
|
|
@@ -150,7 +173,7 @@ module Shellify
|
|
150
173
|
c.description = 'Skip the the previous song in the queue'
|
151
174
|
c.action do
|
152
175
|
@user.player.previous
|
153
|
-
|
176
|
+
print_currently_playing
|
154
177
|
end
|
155
178
|
end
|
156
179
|
|
@@ -158,15 +181,15 @@ module Shellify
|
|
158
181
|
c.description = 'Restart the currently playing song'
|
159
182
|
c.action do
|
160
183
|
@user.player.seek 0
|
161
|
-
|
184
|
+
print_currently_playing
|
162
185
|
end
|
163
186
|
end
|
164
187
|
|
165
188
|
command :seek do |c|
|
166
189
|
c.description = 'Seek to the specified time in the current song'
|
167
|
-
c.action do |args,
|
190
|
+
c.action do |args, _option|
|
168
191
|
@user.player.seek(time_to_ms(args[0]))
|
169
|
-
|
192
|
+
print_currently_playing
|
170
193
|
end
|
171
194
|
end
|
172
195
|
|
@@ -188,17 +211,29 @@ module Shellify
|
|
188
211
|
" Shellify can't perform this action for local tracks"
|
189
212
|
end
|
190
213
|
|
214
|
+
def add_to_collaborative_playlist_message
|
215
|
+
" Shellify can't perform this action for collaborative playlists you don't own"
|
216
|
+
end
|
217
|
+
|
191
218
|
def track_is_local?(track)
|
192
219
|
track.uri.split(':')[1] == 'local'
|
193
220
|
end
|
194
221
|
|
195
|
-
def
|
196
|
-
puts
|
197
|
-
|
198
|
-
"#{
|
199
|
-
"#{
|
200
|
-
"#{
|
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
|
201
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
|
202
237
|
end
|
203
238
|
|
204
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
|