spotify_rec 1.1 → 1.6
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/bin/spotify_rec +33 -26
- data/lib/error.rb +10 -0
- data/lib/login.rb +154 -117
- data/lib/menu.rb +116 -67
- data/lib/my_list.rb +162 -137
- data/lib/playlist.rb +103 -67
- data/lib/rec.rb +54 -44
- data/lib/spec/rec_spec.rb +24 -0
- data/lib/spec/user_spec.rb +34 -0
- data/lib/tutorial.rb +56 -0
- data/lib/user.rb +93 -69
- data/public/users.json +1 -13
- metadata +64 -5
- data/lib/run.rb +0 -47
data/lib/my_list.rb
CHANGED
@@ -1,137 +1,162 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
@
|
6
|
-
@
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def list
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
puts
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MyList
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
@mylist = user.mylist
|
7
|
+
@menu = Menu.new(@user)
|
8
|
+
@prompt = TTY::Prompt.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Display MyList
|
12
|
+
# Displays items in MyList. Raises error if no items in list
|
13
|
+
def list
|
14
|
+
raise MyListEmpty.new, 'List must not be empty' if @mylist.empty?
|
15
|
+
|
16
|
+
list_table
|
17
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
18
|
+
@menu.my_list
|
19
|
+
rescue MyListEmpty
|
20
|
+
empty_list
|
21
|
+
end
|
22
|
+
|
23
|
+
# Generates the list in table view and prints it to the screen
|
24
|
+
def list_table
|
25
|
+
rows = @mylist.map do |hash|
|
26
|
+
if hash['type'] == 'track' || hash['type'] == 'album'
|
27
|
+
["#{hash['name']} by #{hash['artist']}", hash['type'].capitalize]
|
28
|
+
else
|
29
|
+
[hash['name'], hash['type'].capitalize]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
table = Terminal::Table.new headings: %w[Item Type], rows: rows
|
33
|
+
puts table
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tells the user they have no items in their list, and returns to menu
|
37
|
+
def empty_list
|
38
|
+
puts 'Oh no! Your list is currently empty!'.colorize(:light_red)
|
39
|
+
puts 'Add up to 5 items to your list. An item can be a song, artist or genre.'.colorize(:light_red)
|
40
|
+
puts
|
41
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
42
|
+
@menu.my_list
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add to MyList
|
46
|
+
# Prompts the user to specify what type of item to add
|
47
|
+
def add_to_list
|
48
|
+
list_too_long if @mylist.length >= 5
|
49
|
+
selection = @prompt.select('Which type would you like to add?'.colorize(:light_green), %w[Song Artist Genre Back])
|
50
|
+
case_add_to_list(selection)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns user to menu if the MyList has too many items
|
54
|
+
def list_too_long
|
55
|
+
puts "Oh no! You've reached maximum capacity in your list! You won't be able to add".colorize(:light_red)
|
56
|
+
puts 'another item until you remove an existing one.'.colorize(:light_red)
|
57
|
+
puts "You can do this by heading back to the previous menu, and selecting 'Remove'".colorize(:light_red)
|
58
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
59
|
+
@menu.my_list
|
60
|
+
end
|
61
|
+
|
62
|
+
# Cases the add to list selection and routes the user
|
63
|
+
def case_add_to_list(selection)
|
64
|
+
case selection
|
65
|
+
when 'Song'
|
66
|
+
search_song
|
67
|
+
when 'Artist'
|
68
|
+
search_artist
|
69
|
+
when 'Genre'
|
70
|
+
store_genre
|
71
|
+
when 'Back'
|
72
|
+
@menu.my_list
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Prompts user for song name and searches spotify. Prompts user to select a result
|
77
|
+
def search_song
|
78
|
+
song_query = @prompt.ask('What is the name of the song?'.colorize(:light_green))
|
79
|
+
tracks = RSpotify::Track.search(song_query, limit: 5)
|
80
|
+
cleaned_results = []
|
81
|
+
tracks.each { |t| cleaned_results << "#{t.name} by #{t.artists[0].name}" }
|
82
|
+
system('clear')
|
83
|
+
cleaned_results << 'Back'
|
84
|
+
selection = @prompt.select('Please select one of the search results:', cleaned_results).split(' by ')
|
85
|
+
add_to_list if selection[0] == 'Back'
|
86
|
+
store_song(selection)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Stores the song in a hash ready to be written to the file
|
90
|
+
def store_song(details)
|
91
|
+
track = RSpotify::Track.search("#{details[0]} #{details[1]}", limit: 1).first
|
92
|
+
song_details = {
|
93
|
+
'name' => track.name,
|
94
|
+
'artist' => track.artists[0].name,
|
95
|
+
'id' => track.id,
|
96
|
+
'type' => 'track'
|
97
|
+
}
|
98
|
+
@mylist << song_details
|
99
|
+
update_file
|
100
|
+
end
|
101
|
+
|
102
|
+
# Prompts user for artist name, searches for the artist and prompts user to select a result
|
103
|
+
def search_artist
|
104
|
+
artist_query = @prompt.ask('What is the name of the artist?'.colorize(:light_green))
|
105
|
+
artists = RSpotify::Artist.search(artist_query, limit: 5)
|
106
|
+
cleaned_results = []
|
107
|
+
artists.each { |a| cleaned_results << a.name.to_s }
|
108
|
+
system('clear')
|
109
|
+
selection = @prompt.select('Please select one of the search results:', cleaned_results)
|
110
|
+
store_artist(selection)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Stores the artist details in a hash, ready to be written to userfile
|
114
|
+
def store_artist(details)
|
115
|
+
artist = RSpotify::Artist.search(details.to_s, limit: 1).first
|
116
|
+
artist_details = {
|
117
|
+
'name' => artist.name,
|
118
|
+
'id' => artist.id,
|
119
|
+
'type' => 'artist'
|
120
|
+
}
|
121
|
+
@mylist << artist_details
|
122
|
+
update_file
|
123
|
+
end
|
124
|
+
|
125
|
+
# Prompts user to select genre from the list. Stores details in hash to be written to file
|
126
|
+
def store_genre
|
127
|
+
genres = RSpotify::Recommendations.available_genre_seeds
|
128
|
+
genre = @prompt.select('Which genre would you like to add to your list?', genres, filter: true)
|
129
|
+
genre_details = {
|
130
|
+
'name' => genre.capitalize,
|
131
|
+
'type' => 'genre'
|
132
|
+
}
|
133
|
+
@mylist << genre_details
|
134
|
+
update_file
|
135
|
+
end
|
136
|
+
|
137
|
+
# Remove From MyList
|
138
|
+
# Prompts user to select item to remove from MyList. Removes item from mylist array
|
139
|
+
def remove_from_list
|
140
|
+
empty_list if @mylist.length <= 0
|
141
|
+
item_names = @mylist.map { |item| item['name'] }
|
142
|
+
item_names << 'Back'
|
143
|
+
selection = @prompt.select('Which item would you like to remove?'.colorize(:light_green), item_names)
|
144
|
+
@menu.my_list if selection == 'Back'
|
145
|
+
@mylist.each_with_index do |item, index|
|
146
|
+
@mylist.delete_at(index) if item['name'] == selection
|
147
|
+
end
|
148
|
+
update_file
|
149
|
+
end
|
150
|
+
|
151
|
+
# Update userfile
|
152
|
+
# Writes the user MyList to the userfile
|
153
|
+
def update_file
|
154
|
+
updated_data = Login.load_data.each { |user| user['mylist'] = @mylist if user['id'] == @user.uid.to_s }
|
155
|
+
File.open(userdata, 'w') do |f|
|
156
|
+
f.puts JSON.pretty_generate(updated_data)
|
157
|
+
end
|
158
|
+
puts 'Sweet! Your list has been updated!'.colorize(:light_green)
|
159
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
160
|
+
@menu.my_list
|
161
|
+
end
|
162
|
+
end
|
data/lib/playlist.rb
CHANGED
@@ -1,112 +1,148 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
class Playlist
|
3
4
|
attr_reader :tracks
|
4
5
|
|
5
6
|
def initialize(user)
|
6
|
-
@playlist = user.playlist
|
7
7
|
@user = user
|
8
|
+
@playlist = user.playlist
|
9
|
+
@prompt = TTY::Prompt.new
|
8
10
|
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
# Playlist Menu Section
|
13
|
+
# Prompts user to select an item from the menu
|
14
|
+
def menu
|
15
|
+
system('clear')
|
16
|
+
selection = @prompt.select('》 PLAYLIST 《', ['Display', 'Add', 'Remove', 'Export To File', 'Back'])
|
17
|
+
case selection
|
18
|
+
when 'Display'
|
19
|
+
list
|
20
|
+
else
|
21
|
+
case_menu(selection)
|
22
|
+
end
|
14
23
|
end
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
# Cases the user selection and routes the user
|
26
|
+
def case_menu(selection)
|
27
|
+
case selection
|
28
|
+
when 'Add'
|
29
|
+
add
|
30
|
+
when 'Remove'
|
31
|
+
remove
|
32
|
+
else
|
33
|
+
second_case_menu(selection)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Continues to case the selection and route the user
|
38
|
+
def second_case_menu(selection)
|
39
|
+
case selection
|
40
|
+
when 'Export To File'
|
41
|
+
export_to_file
|
42
|
+
when 'Back'
|
43
|
+
menu = Menu.new(@user)
|
44
|
+
menu.menu_router
|
24
45
|
end
|
25
|
-
update_playlist
|
26
46
|
end
|
27
47
|
|
48
|
+
# View Playlist
|
49
|
+
# Generates table from user playlist and prints it to the screen
|
50
|
+
def list
|
51
|
+
puts '》 PLAYLIST 《'
|
52
|
+
empty if @playlist.empty?
|
53
|
+
rows = @playlist.map { |track| [track['name'], track['artist']] }
|
54
|
+
table = Terminal::Table.new headings: %w[Track Artist], rows: rows
|
55
|
+
puts table
|
56
|
+
keypress_playlist
|
57
|
+
menu
|
58
|
+
end
|
59
|
+
|
60
|
+
# Tells user if their playlist is empty and sends them back to the menu
|
28
61
|
def empty
|
29
|
-
puts
|
30
|
-
puts
|
31
|
-
|
32
|
-
|
33
|
-
|
62
|
+
puts 'Oh no! Your playlist is currently empty!'
|
63
|
+
puts 'You can add songs manually from the previous menu, or generate recommendations and add those!'
|
64
|
+
puts
|
65
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
66
|
+
menu
|
34
67
|
end
|
35
68
|
|
69
|
+
# Add to Playlist
|
70
|
+
# Prompts user to enter song name, searches for song and prompts user to select a result
|
36
71
|
def add
|
37
|
-
song_query =
|
72
|
+
song_query = @prompt.ask('What is the name of the song?')
|
38
73
|
tracks = RSpotify::Track.search(song_query, limit: 5)
|
39
74
|
cleaned_results = []
|
40
75
|
tracks.each { |t| cleaned_results << "#{t.name} by #{t.artists[0].name}" }
|
41
|
-
system(
|
42
|
-
cleaned_results <<
|
43
|
-
selection =
|
44
|
-
menu if selection[0] ==
|
76
|
+
system('clear')
|
77
|
+
cleaned_results << 'Back'
|
78
|
+
selection = @prompt.select('Please select one of the search results:', cleaned_results).split(' by ')
|
79
|
+
menu if selection[0] == 'Back'
|
45
80
|
store(selection)
|
46
81
|
end
|
47
82
|
|
83
|
+
# Turns song details into a hash and adds to playlist array
|
48
84
|
def store(details)
|
49
85
|
track = RSpotify::Track.search("#{details[0]} #{details[1]}", limit: 1).first
|
50
86
|
song_details = {
|
51
|
-
|
52
|
-
|
53
|
-
|
87
|
+
'name' => track.name,
|
88
|
+
'id' => track.id,
|
89
|
+
'artist' => track.artists[0].name
|
54
90
|
}
|
55
91
|
@playlist << song_details
|
56
92
|
update_playlist
|
57
93
|
end
|
58
94
|
|
95
|
+
# Remove from Playlist
|
96
|
+
# Prompts user to select item in playlist to remove and deleted from playlist array
|
97
|
+
def remove
|
98
|
+
empty if @playlist.length <= 0
|
99
|
+
item_names = @playlist.map { |item| "#{item['name']} by #{item['artist']}" }
|
100
|
+
item_names << 'Back'
|
101
|
+
selection = @prompt.select('Which track would you like to remove?', item_names).split(' by ')
|
102
|
+
menu if selection == 'Back'
|
103
|
+
@playlist.each_with_index do |item, index|
|
104
|
+
@user.playlist.delete_at(index) if item['name'] == selection[0]
|
105
|
+
end
|
106
|
+
update_playlist
|
107
|
+
end
|
108
|
+
|
109
|
+
# Update Playlist in file
|
110
|
+
# Loads userdata and updates playlist in file. Returns the user to the menu
|
59
111
|
def update_playlist
|
60
|
-
updated_data = Login.load_data.each { |user| user[
|
61
|
-
File.open(
|
112
|
+
updated_data = Login.load_data.each { |user| user['playlist'] = @playlist if user['id'] == @user.uid.to_s }
|
113
|
+
File.open(Login.userdata, 'w') do |f|
|
62
114
|
f.puts JSON.pretty_generate(updated_data)
|
63
115
|
end
|
64
|
-
puts
|
65
|
-
|
116
|
+
puts 'Sweet! Your playlist has been updated!'
|
117
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
66
118
|
menu
|
67
119
|
end
|
68
120
|
|
121
|
+
# Export playlist to file
|
122
|
+
# Writes user playlist information to a markdown file
|
69
123
|
def export_to_file
|
70
124
|
path = File.join(File.dirname(File.dirname(File.absolute_path(__FILE__))))
|
71
|
-
|
72
|
-
File.open(playlist_file,"w") do |f|
|
125
|
+
File.open("#{path}/playlist.md", 'w') do |f|
|
73
126
|
f.puts("# #{@user.username}'s Playlist")
|
74
|
-
@playlist.each
|
127
|
+
@playlist.each do |track|
|
128
|
+
link = "[Listen on Spotify](https://open.spotify.com/track/#{track['id']})"
|
129
|
+
f.puts("1. #{track['name']} by #{track['artist']} #{link}")
|
130
|
+
end
|
75
131
|
end
|
76
|
-
|
77
|
-
puts "Exported playlist to your Desktop!"
|
78
|
-
sleep(2)
|
79
|
-
system("clear")
|
132
|
+
copy_to_desktop(path)
|
80
133
|
end
|
81
134
|
|
82
|
-
|
83
|
-
|
135
|
+
# Copies markdown file to desktop for easy access
|
136
|
+
def copy_to_desktop(path)
|
137
|
+
system("cp #{path}/playlist.md ~/Desktop/playlist.md")
|
138
|
+
puts 'Exported playlist to your Desktop!'
|
139
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
140
|
+
menu
|
84
141
|
end
|
85
142
|
|
86
|
-
|
87
|
-
system("clear")
|
88
|
-
selection = $prompt.select("》 PLAYLIST 《", (["Display", "Add", "Remove", "Export To File", "Back"]))
|
89
|
-
case selection
|
90
|
-
when "Display"
|
91
|
-
puts "》 PLAYLIST 《"
|
92
|
-
list
|
93
|
-
keypress_playlist
|
94
|
-
menu
|
95
|
-
when "Add"
|
96
|
-
add
|
97
|
-
keypress_playlist
|
98
|
-
menu
|
99
|
-
when "Remove"
|
100
|
-
remove
|
101
|
-
menu
|
102
|
-
when "Export To File"
|
103
|
-
export_to_file
|
104
|
-
keypress_playlist
|
105
|
-
menu
|
106
|
-
when "Back"
|
107
|
-
@menu = Menu.new(@user)
|
108
|
-
@menu.menu_router
|
109
|
-
end
|
110
|
-
end
|
143
|
+
# Helper method
|
111
144
|
|
112
|
-
|
145
|
+
def keypress_playlist
|
146
|
+
@prompt.keypress('Press any key to return to the previous menu..')
|
147
|
+
end
|
148
|
+
end
|