spotify_rec 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/spotify_rec +54 -0
- data/lib/login.rb +117 -0
- data/lib/menu.rb +67 -0
- data/lib/my_list.rb +137 -0
- data/lib/playlist.rb +112 -0
- data/lib/rec.rb +65 -0
- data/lib/run.rb +47 -0
- data/lib/user.rb +69 -0
- data/public/users.json +13 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '00749e0a49528b3d680597ee48924685869ab8963027609a951f0d8e1bd6bfd6'
|
4
|
+
data.tar.gz: ead9a411308f5234a5b4d1919201e0721f1191304a3e486e5f75e644db23fe1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88011906853f7d6e5e6e4fe650c5d474f0caa8534ee8ee590e7bd5178b48bf773a4b56c9d12f9fee97db0ce8affb8d7860a783330290ef4024c68e91a0aa43c7
|
7
|
+
data.tar.gz: cd0e528637accbbd6c8a15b6a6de7d58e8b21e04cabe9c73787c9fb8885db2056b27075a6fefd199be3a40924f050f53aec97be5e2bbe1d58d32684700d7dd39
|
data/bin/spotify_rec
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# gems
|
4
|
+
require 'rspotify'
|
5
|
+
require 'optparse'
|
6
|
+
require 'json'
|
7
|
+
require 'tty-prompt'
|
8
|
+
require 'terminal-table'
|
9
|
+
require 'colorize'
|
10
|
+
|
11
|
+
# files
|
12
|
+
require_relative '../lib/user'
|
13
|
+
require_relative '../lib/playlist'
|
14
|
+
require_relative '../lib/login'
|
15
|
+
require_relative '../lib/rec'
|
16
|
+
require_relative '../lib/menu'
|
17
|
+
require_relative '../lib/my_list'
|
18
|
+
|
19
|
+
#modules
|
20
|
+
include Login
|
21
|
+
|
22
|
+
RSpotify.authenticate("712ff89a218a4e6dbe1f169e06f949b9", "e9e0517f405b4a01a1be8823126459b7")
|
23
|
+
$prompt = TTY::Prompt.new
|
24
|
+
|
25
|
+
VERSION = 1.0
|
26
|
+
|
27
|
+
ARGV << '--run' if ARGV.empty?
|
28
|
+
|
29
|
+
options = {}
|
30
|
+
parser = OptionParser.new do |opts|
|
31
|
+
opts.banner = "Welcome to Spotify Recommendations! Usage: spotifyrec [options]".colorize(:light_green)
|
32
|
+
|
33
|
+
opts.on("-v", "--version", "Display the version") do
|
34
|
+
puts "Spotify Recommendations version #{VERSION}".colorize(:light_green)
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-h", "--help", "Display the help message") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-qGENRE", "--quick=GENRE", "Generate a quick recommendation with the chosen genre") do |genre|
|
43
|
+
recommendations = RSpotify::Recommendations.generate(limit: 5, seed_genres: [genre])
|
44
|
+
cleaned_recs = recommendations.tracks.map { |t| "#{t.name} by #{t.artists[0].name}" }
|
45
|
+
puts "》 RECOMMENDATIONS 《".colorize(:light_green)
|
46
|
+
cleaned_recs.each { |track| puts track }
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("-r", "--run", "Run the application") do
|
50
|
+
Login::login
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
parser.parse!
|
data/lib/login.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require_relative 'menu'
|
2
|
+
|
3
|
+
module Login
|
4
|
+
|
5
|
+
def user
|
6
|
+
return @user
|
7
|
+
end
|
8
|
+
|
9
|
+
def userdata
|
10
|
+
path = File.join(File.dirname(File.dirname(File.absolute_path(__FILE__))))
|
11
|
+
userfile = "#{path}/public/users.json"
|
12
|
+
return userfile
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
system("clear")
|
17
|
+
end
|
18
|
+
|
19
|
+
def initial_login
|
20
|
+
clear
|
21
|
+
puts " ;;;;;;;;;;;;;;;;;;;
|
22
|
+
;;;;;;;;;;;;;;;;;;;
|
23
|
+
; ;
|
24
|
+
; ;
|
25
|
+
; ;
|
26
|
+
; ;
|
27
|
+
; ;
|
28
|
+
; ;
|
29
|
+
,;;;;; ,;;;;;
|
30
|
+
;;;;;; ;;;;;;
|
31
|
+
`;;;;' `;;;;'
|
32
|
+
".colorize(:light_green)
|
33
|
+
puts "》 Welcome to the Spotify Recommendations App! 《".colorize(:light_green)
|
34
|
+
puts
|
35
|
+
return $prompt.select("Are you a new or returning user? \n", %w(New Returning))
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_data
|
39
|
+
file = File.read(userdata)
|
40
|
+
return JSON.parse(file)
|
41
|
+
end
|
42
|
+
|
43
|
+
def gen_uid
|
44
|
+
return load_data.length + 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def store_user
|
48
|
+
data = load_data
|
49
|
+
user_details = {
|
50
|
+
"id" => "#{@user.uid}",
|
51
|
+
"username" => @user.username,
|
52
|
+
"password" => @user.password,
|
53
|
+
"playlist" => @user.playlist,
|
54
|
+
"mylist" => @user.mylist
|
55
|
+
}
|
56
|
+
data << user_details
|
57
|
+
File.open(userdata,"w") do |f|
|
58
|
+
f.puts JSON.pretty_generate(data)
|
59
|
+
end
|
60
|
+
puts "You're now registered! Logging you in..".colorize(:light_green)
|
61
|
+
sleep(1)
|
62
|
+
@menu = Menu.new(@user)
|
63
|
+
@menu.menu_router
|
64
|
+
end
|
65
|
+
|
66
|
+
def new_user
|
67
|
+
system("clear")
|
68
|
+
puts "Welcome! Please register for an account to continue.".colorize(:light_green)
|
69
|
+
username = $prompt.ask("Username >") { |u| u.validate(/\A[0-9a-zA-Z'-]*\z/, "Username must only contain letters and numbers")}
|
70
|
+
password = $prompt.mask("Password >")
|
71
|
+
@user = User.new(username, password, gen_uid)
|
72
|
+
@user_playlist = Playlist.new(@user)
|
73
|
+
store_user
|
74
|
+
end
|
75
|
+
|
76
|
+
def returning_user
|
77
|
+
system("clear")
|
78
|
+
puts "Welcome back! Please login to continue.".colorize(:light_green)
|
79
|
+
username = $prompt.ask("Username >")
|
80
|
+
password= $prompt.mask("Password >")
|
81
|
+
authenticate(username, password)
|
82
|
+
end
|
83
|
+
|
84
|
+
def authenticate(username, password)
|
85
|
+
data = load_data
|
86
|
+
auth = false
|
87
|
+
data.each do |hash|
|
88
|
+
if hash["username"].downcase == username.downcase
|
89
|
+
if hash["password"] == password
|
90
|
+
auth = true
|
91
|
+
@user = User.new(username, password, hash["id"], hash["playlist"], hash["mylist"])
|
92
|
+
@user_playlist = Playlist.new(@user)
|
93
|
+
puts "Success! Logging you in..".colorize(:light_green)
|
94
|
+
sleep(1)
|
95
|
+
@menu = Menu.new(@user)
|
96
|
+
@menu.menu_router
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
unless auth
|
101
|
+
puts "Incorrect username or password!".colorize(:red)
|
102
|
+
sleep(1)
|
103
|
+
returning_user
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def login
|
108
|
+
new_returning = initial_login.downcase
|
109
|
+
if new_returning == "new"
|
110
|
+
new_user
|
111
|
+
end
|
112
|
+
if new_returning == "returning"
|
113
|
+
returning_user
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
data/lib/menu.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
class Menu
|
2
|
+
|
3
|
+
def initialize(user)
|
4
|
+
@user = user
|
5
|
+
@prompt = prompt = TTY::Prompt.new
|
6
|
+
@playlist = Playlist.new(@user)
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_menu
|
10
|
+
system("clear")
|
11
|
+
return @prompt.select("》 MAIN MENU 《\n".colorize(:light_green), (["My List", "Recommendations", "Playlist", "Account Details", "Exit"]))
|
12
|
+
end
|
13
|
+
|
14
|
+
def my_list
|
15
|
+
system("clear")
|
16
|
+
selection = @prompt.select("》 MY LIST 《\n".colorize(:light_green), (["Display", "Add", "Remove", "Back"]))
|
17
|
+
mylist = MyList.new(@user)
|
18
|
+
case selection
|
19
|
+
when "Display"
|
20
|
+
mylist.list
|
21
|
+
when "Add"
|
22
|
+
mylist.add_to_list
|
23
|
+
when "Remove"
|
24
|
+
mylist.remove_from_list
|
25
|
+
when "Back"
|
26
|
+
menu_router
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def account_details
|
31
|
+
system("clear")
|
32
|
+
selection = @prompt.select("》 ACCOUNT MENU 《 \n".colorize(:light_green), (["View Details", "Change Username", "Change Password", "Delete Account", "Back"]))
|
33
|
+
case selection
|
34
|
+
when "View Details"
|
35
|
+
@user.details
|
36
|
+
when "Change Username"
|
37
|
+
username = @prompt.ask("Please enter your new username >") { |u| u.validate(/\A[0-9a-zA-Z'-]*\z/, "Username must only contain letters and numbers")}
|
38
|
+
@user.change_username(username)
|
39
|
+
when "Change Password"
|
40
|
+
password = @prompt.ask("Please enter your new password >")
|
41
|
+
@user.change_password(password)
|
42
|
+
when "Delete Account"
|
43
|
+
@user.delete_account
|
44
|
+
when "Back"
|
45
|
+
menu_router
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def menu_router
|
50
|
+
selection = display_menu
|
51
|
+
case selection
|
52
|
+
when "My List"
|
53
|
+
system("clear")
|
54
|
+
my_list
|
55
|
+
when "Recommendations"
|
56
|
+
recommendation = Rec.new(@user)
|
57
|
+
recommendation.amount_of_suggestions
|
58
|
+
when "Playlist"
|
59
|
+
@playlist.menu
|
60
|
+
when "Account Details"
|
61
|
+
account_details
|
62
|
+
when "Exit"
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/my_list.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
class MyList
|
2
|
+
|
3
|
+
def initialize(user)
|
4
|
+
@user = user
|
5
|
+
@mylist = user.mylist
|
6
|
+
@menu = Menu.new(@user)
|
7
|
+
end
|
8
|
+
|
9
|
+
def separator
|
10
|
+
puts "----------------------------------------"
|
11
|
+
end
|
12
|
+
|
13
|
+
def list
|
14
|
+
unless @mylist.empty?
|
15
|
+
rows = @mylist.map do |hash|
|
16
|
+
if hash["type"] == "track" || hash["type"] == "album"
|
17
|
+
["#{hash["name"]} by #{hash["artist"]}", hash["type"].capitalize]
|
18
|
+
else
|
19
|
+
[hash["name"], hash["type"].capitalize]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
table = Terminal::Table.new :headings => ['Item', 'Type'], :rows => rows
|
23
|
+
puts table
|
24
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
25
|
+
@menu.my_list
|
26
|
+
else
|
27
|
+
empty_list
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def empty_list
|
32
|
+
puts "Oh no! Your list is currently empty!".colorize(:light_red)
|
33
|
+
puts "Add up to 5 items to your list. An item can be a song, artist or genre.".colorize(:light_red)
|
34
|
+
separator
|
35
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
36
|
+
@menu.my_list
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_to_list
|
40
|
+
if @mylist.length >= 5
|
41
|
+
puts "Oh no! You've reached maximum capacity in your list! You won't be able to add another item until you remove an existing one.".colorize(:light_red)
|
42
|
+
puts "You can do this by heading back to the previous menu, and selecting 'Remove'".colorize(:light_red)
|
43
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
44
|
+
@menu.my_list
|
45
|
+
end
|
46
|
+
selection = $prompt.select("Which type would you like to add?".colorize(:light_green), (["Song", "Artist", "Genre", "Back"]))
|
47
|
+
case selection
|
48
|
+
when "Song"
|
49
|
+
search_song
|
50
|
+
when "Artist"
|
51
|
+
search_artist
|
52
|
+
when "Genre"
|
53
|
+
store_genre
|
54
|
+
when "Back"
|
55
|
+
@menu.my_list
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_from_list
|
60
|
+
empty_list if @mylist.length <= 0
|
61
|
+
item_names = @mylist.map { |item| item["name"] }
|
62
|
+
item_names << "Back"
|
63
|
+
selection = $prompt.select("Which item would you like to remove?".colorize(:light_green), (item_names))
|
64
|
+
@menu.my_list if selection == "Back"
|
65
|
+
@mylist.each_with_index do |item, index|
|
66
|
+
@mylist.delete_at(index) if item["name"] == selection
|
67
|
+
end
|
68
|
+
update_file
|
69
|
+
end
|
70
|
+
|
71
|
+
def search_song
|
72
|
+
song_query = $prompt.ask("What is the name of the song?".colorize(:light_green))
|
73
|
+
tracks = RSpotify::Track.search(song_query, limit: 5)
|
74
|
+
cleaned_results = []
|
75
|
+
tracks.each { |t| cleaned_results << "#{t.name} by #{t.artists[0].name}" }
|
76
|
+
system("clear")
|
77
|
+
cleaned_results << "Back"
|
78
|
+
selection = $prompt.select("Please select one of the search results:", (cleaned_results)).split(" by ")
|
79
|
+
add_to_list if selection[0] == "Back"
|
80
|
+
store_song(selection)
|
81
|
+
end
|
82
|
+
|
83
|
+
def store_song(details)
|
84
|
+
track = RSpotify::Track.search("#{details[0]} #{details[1]}", limit: 1).first
|
85
|
+
song_details = {
|
86
|
+
"name" => track.name,
|
87
|
+
"artist" => track.artists[0].name,
|
88
|
+
"id" => track.id,
|
89
|
+
"type" => "track"
|
90
|
+
}
|
91
|
+
@mylist << song_details
|
92
|
+
update_file
|
93
|
+
end
|
94
|
+
|
95
|
+
def search_artist
|
96
|
+
artist_query = $prompt.ask("What is the name of the artist?".colorize(:light_green))
|
97
|
+
artists = RSpotify::Artist.search(artist_query, limit: 5)
|
98
|
+
cleaned_results = []
|
99
|
+
artists.each { |a| cleaned_results << "#{a.name}" }
|
100
|
+
system("clear")
|
101
|
+
selection = $prompt.select("Please select one of the search results:", (cleaned_results))
|
102
|
+
store_artist(selection)
|
103
|
+
end
|
104
|
+
|
105
|
+
def store_artist(details)
|
106
|
+
artist = RSpotify::Artist.search("#{details}", limit: 1).first
|
107
|
+
artist_details = {
|
108
|
+
"name" => artist.name,
|
109
|
+
"id" => artist.id,
|
110
|
+
"type" => "artist"
|
111
|
+
}
|
112
|
+
@mylist << artist_details
|
113
|
+
update_file
|
114
|
+
end
|
115
|
+
|
116
|
+
def store_genre
|
117
|
+
genres = RSpotify::Recommendations.available_genre_seeds
|
118
|
+
genre = $prompt.select("Which genre would you like to add to your list?".colorize(:light_green), genres, filter: true)
|
119
|
+
genre_details = {
|
120
|
+
"name" => genre.capitalize,
|
121
|
+
"type" => "genre"
|
122
|
+
}
|
123
|
+
@mylist << genre_details
|
124
|
+
update_file
|
125
|
+
end
|
126
|
+
|
127
|
+
def update_file
|
128
|
+
updated_data = Login.load_data.each { |user| user["mylist"] = @mylist if user["id"] == @user.uid.to_s }
|
129
|
+
File.open(userdata,"w") do |f|
|
130
|
+
f.puts JSON.pretty_generate(updated_data)
|
131
|
+
end
|
132
|
+
puts "Sweet! Your list has been updated!".colorize(:light_green)
|
133
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
134
|
+
@menu.my_list
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
data/lib/playlist.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
class Playlist
|
2
|
+
|
3
|
+
attr_reader :tracks
|
4
|
+
|
5
|
+
def initialize(user)
|
6
|
+
@playlist = user.playlist
|
7
|
+
@user = user
|
8
|
+
end
|
9
|
+
|
10
|
+
def list
|
11
|
+
rows = @playlist.map { |track| [track["name"], track["artist"]] }
|
12
|
+
table = Terminal::Table.new :headings => ['Track', 'Artist'], :rows => rows
|
13
|
+
puts table
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove
|
17
|
+
empty if @playlist.length <= 0
|
18
|
+
item_names = @playlist.map { |item| "#{item["name"]} by #{item["artist"]}" }
|
19
|
+
item_names << "Back"
|
20
|
+
selection = $prompt.select("Which track would you like to remove?", (item_names)).split(" by ")
|
21
|
+
menu if selection == "Back"
|
22
|
+
@playlist.each_with_index do |item, index|
|
23
|
+
@user.playlist.delete_at(index) if item["name"] == selection[0]
|
24
|
+
end
|
25
|
+
update_playlist
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty
|
29
|
+
puts "Oh no! Your playlist is currently empty!"
|
30
|
+
puts "You can add songs manually from the previous menu, or generate recommendations and add those!"
|
31
|
+
separator
|
32
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
33
|
+
Menu::my_list
|
34
|
+
end
|
35
|
+
|
36
|
+
def add
|
37
|
+
song_query = $prompt.ask("What is the name of the song?")
|
38
|
+
tracks = RSpotify::Track.search(song_query, limit: 5)
|
39
|
+
cleaned_results = []
|
40
|
+
tracks.each { |t| cleaned_results << "#{t.name} by #{t.artists[0].name}" }
|
41
|
+
system("clear")
|
42
|
+
cleaned_results << "Back"
|
43
|
+
selection = $prompt.select("Please select one of the search results:", (cleaned_results)).split(" by ")
|
44
|
+
menu if selection[0] == "Back"
|
45
|
+
store(selection)
|
46
|
+
end
|
47
|
+
|
48
|
+
def store(details)
|
49
|
+
track = RSpotify::Track.search("#{details[0]} #{details[1]}", limit: 1).first
|
50
|
+
song_details = {
|
51
|
+
"name" => track.name,
|
52
|
+
"id" => track.id,
|
53
|
+
"artist" => track.artists[0].name
|
54
|
+
}
|
55
|
+
@playlist << song_details
|
56
|
+
update_playlist
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_playlist
|
60
|
+
updated_data = Login.load_data.each { |user| user["playlist"] = @playlist if user["id"] == @user.uid.to_s }
|
61
|
+
File.open((Login::userdata),"w") do |f|
|
62
|
+
f.puts JSON.pretty_generate(updated_data)
|
63
|
+
end
|
64
|
+
puts "Sweet! Your playlist has been updated!"
|
65
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
66
|
+
menu
|
67
|
+
end
|
68
|
+
|
69
|
+
def export_to_file
|
70
|
+
path = File.join(File.dirname(File.dirname(File.absolute_path(__FILE__))))
|
71
|
+
playlist_file = "#{path}/playlist.md"
|
72
|
+
File.open(playlist_file,"w") do |f|
|
73
|
+
f.puts("# #{@user.username}'s Playlist")
|
74
|
+
@playlist.each { |track| f.puts("1. #{track["name"]} by #{track["artist"]} || [Listen on Spotify](https://open.spotify.com/track/#{track["id"]})") }
|
75
|
+
end
|
76
|
+
system("cp #{playlist_file} ~/Desktop/playlist.md")
|
77
|
+
puts "Exported playlist to your Desktop!"
|
78
|
+
sleep(2)
|
79
|
+
system("clear")
|
80
|
+
end
|
81
|
+
|
82
|
+
def keypress_playlist
|
83
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
84
|
+
end
|
85
|
+
|
86
|
+
def menu
|
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
|
111
|
+
|
112
|
+
end
|
data/lib/rec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
class Rec
|
2
|
+
|
3
|
+
def initialize(user)
|
4
|
+
@user_list = user.mylist
|
5
|
+
@user = user
|
6
|
+
end
|
7
|
+
|
8
|
+
def sort_my_list
|
9
|
+
@tracks = []
|
10
|
+
@artists = []
|
11
|
+
@genres = []
|
12
|
+
@user_list.each do |item|
|
13
|
+
@tracks << item["id"] if item["type"] == "track"
|
14
|
+
@artists << item["id"] if item["type"] == "artist"
|
15
|
+
@genres << item["name"].downcase if item["type"] == "genre"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_file
|
20
|
+
updated_data = Login.load_data.each { |user| user["playlist"] = @user.playlist if user["id"] == @user.uid.to_s }
|
21
|
+
File.open(userdata,"w") do |f|
|
22
|
+
f.puts JSON.pretty_generate(updated_data)
|
23
|
+
end
|
24
|
+
puts "Sweet! Your list has been updated!".colorize(:light_green)
|
25
|
+
$prompt.keypress("Press any key to return to the previous menu..")
|
26
|
+
menu = Menu.new(@user)
|
27
|
+
menu.menu_router
|
28
|
+
end
|
29
|
+
|
30
|
+
def clean_recommendations(selections)
|
31
|
+
selections.each do |track|
|
32
|
+
details = track.split(" by ")
|
33
|
+
song = RSpotify::Track.search("#{details[0]} #{details[1]}", limit: 1).first
|
34
|
+
song_details = {
|
35
|
+
"name" => song.name,
|
36
|
+
"id" => song.id,
|
37
|
+
"artist" => song.artists[0].name }
|
38
|
+
@user.playlist << song_details
|
39
|
+
end
|
40
|
+
update_file
|
41
|
+
end
|
42
|
+
|
43
|
+
def recommend(num)
|
44
|
+
system("clear")
|
45
|
+
sort_my_list
|
46
|
+
recommendations = RSpotify::Recommendations.generate(limit: num, seed_artists: @artists, seed_genres: @genres, seed_tracks: @tracks)
|
47
|
+
cleaned_recs = recommendations.tracks.map { |t| "#{t.name} by #{t.artists[0].name}" }
|
48
|
+
puts "》 RECOMMENDATIONS 《".colorize(:light_green)
|
49
|
+
puts "Select the recommendations you want to add to your playlist!"
|
50
|
+
selections = $prompt.multi_select("Hit enter with none selected to skip.", cleaned_recs)
|
51
|
+
clean_recommendations(selections)
|
52
|
+
end
|
53
|
+
|
54
|
+
def amount_of_suggestions
|
55
|
+
system("clear")
|
56
|
+
puts "》 RECOMMENDATIONS 《"
|
57
|
+
amount = $prompt.ask("How many recommendations would you like to generate?".colorize(:light_green)) do |q|
|
58
|
+
q.in '1-10'
|
59
|
+
q.messages[:range?] = "Number must be between 1 and 10"
|
60
|
+
end
|
61
|
+
recommend(amount.to_i)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
data/lib/run.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rspotify'
|
2
|
+
require 'optparse'
|
3
|
+
require 'json'
|
4
|
+
require 'tty-prompt'
|
5
|
+
require 'terminal-table'
|
6
|
+
require 'colorize'
|
7
|
+
require_relative 'user'
|
8
|
+
require_relative 'playlist'
|
9
|
+
require_relative 'login'
|
10
|
+
require_relative 'rec'
|
11
|
+
require_relative 'menu'
|
12
|
+
require_relative 'my_list'
|
13
|
+
include Login
|
14
|
+
|
15
|
+
RSpotify.authenticate("712ff89a218a4e6dbe1f169e06f949b9", "e9e0517f405b4a01a1be8823126459b7")
|
16
|
+
$prompt = TTY::Prompt.new
|
17
|
+
|
18
|
+
VERSION = 1.0
|
19
|
+
|
20
|
+
ARGV << '--run' if ARGV.empty?
|
21
|
+
|
22
|
+
options = {}
|
23
|
+
parser = OptionParser.new do |opts|
|
24
|
+
opts.banner = "Welcome to Spotify Recommendations! Usage: spotifyrec [options]".colorize(:light_green)
|
25
|
+
|
26
|
+
opts.on("-v", "--version", "Display the version") do
|
27
|
+
puts "Spotify Recommendations version #{VERSION}".colorize(:light_green)
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on("-h", "--help", "Display the help message") do
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-qGENRE", "--quick=GENRE", "Generate a quick recommendation with the chosen genre") do |genre|
|
36
|
+
recommendations = RSpotify::Recommendations.generate(limit: 5, seed_genres: [genre])
|
37
|
+
cleaned_recs = recommendations.tracks.map { |t| "#{t.name} by #{t.artists[0].name}" }
|
38
|
+
puts "》 RECOMMENDATIONS 《".colorize(:light_green)
|
39
|
+
cleaned_recs.each { |track| puts track }
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-r", "--run", "Run the application") do
|
43
|
+
Login::login
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
parser.parse!
|
data/lib/user.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
class User
|
2
|
+
|
3
|
+
attr_reader :username, :password, :uid
|
4
|
+
attr_accessor :playlist, :mylist
|
5
|
+
|
6
|
+
def initialize(username, password, uid, playlist=[], mylist=[])
|
7
|
+
@username = username
|
8
|
+
@password = password
|
9
|
+
@playlist = playlist
|
10
|
+
@uid = uid
|
11
|
+
@mylist = mylist
|
12
|
+
end
|
13
|
+
|
14
|
+
def details
|
15
|
+
puts "Username: #{@username.colorize(:light_green)}"
|
16
|
+
puts "Password: #{@password.colorize(:light_green)}"
|
17
|
+
puts "User ID: #{@uid.to_s.colorize(:light_green)}"
|
18
|
+
$prompt.keypress("Press any key to continue..")
|
19
|
+
menu = Menu.new(Login::user)
|
20
|
+
menu.account_details
|
21
|
+
end
|
22
|
+
|
23
|
+
def change_username(new_name)
|
24
|
+
updated_data = Login.load_data.each { |user| user["username"] = new_name if user["id"] == Login::user.uid.to_s }
|
25
|
+
File.open((Login::userdata),"w") do |f|
|
26
|
+
f.puts JSON.pretty_generate(updated_data)
|
27
|
+
end
|
28
|
+
@username = new_name
|
29
|
+
puts "Success! Your new username is #{Login::user.username}".colorize(:light_green)
|
30
|
+
$prompt.keypress("Press any key to continue..")
|
31
|
+
menu = Menu.new(Login::user)
|
32
|
+
menu.account_details
|
33
|
+
end
|
34
|
+
|
35
|
+
def change_password(new_password)
|
36
|
+
updated_data = Login.load_data.each { |user| user["password"] = new_password if user["id"] == Login::user.uid.to_s }
|
37
|
+
File.open((Login::userdata),"w") do |f|
|
38
|
+
f.puts JSON.pretty_generate(updated_data)
|
39
|
+
end
|
40
|
+
@password = new_password
|
41
|
+
puts "Success! Your password has been changed.".colorize(:light_green)
|
42
|
+
$prompt.keypress("Press any key to continue..")
|
43
|
+
menu = Menu.new(Login::user)
|
44
|
+
menu.account_details
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete_from_file
|
48
|
+
updated_data = []
|
49
|
+
Login.load_data.each { |user| updated_data << user unless user["id"] == Login::user.uid.to_s }
|
50
|
+
File.open((Login::userdata),"w") do |f|
|
51
|
+
f.puts JSON.pretty_generate(updated_data)
|
52
|
+
end
|
53
|
+
puts "Your account has been deleted. The program will now exit.".colorize(:light_red)
|
54
|
+
$prompt.keypress("Press any key to continue..")
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def delete_account
|
60
|
+
puts "Woah there! Deleting your account is serious business, and cannot be undone.".colorize(:light_red)
|
61
|
+
selection = $prompt.yes?("Are you sure you want to delete your account?").colorize(:light_red)
|
62
|
+
unless selection
|
63
|
+
menu = Menu.new(Login::user)
|
64
|
+
else
|
65
|
+
delete_from_file
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/public/users.json
ADDED
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spotify_rec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Ahale
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-28 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables:
|
16
|
+
- spotify_rec
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/spotify_rec
|
21
|
+
- lib/login.rb
|
22
|
+
- lib/menu.rb
|
23
|
+
- lib/my_list.rb
|
24
|
+
- lib/playlist.rb
|
25
|
+
- lib/rec.rb
|
26
|
+
- lib/run.rb
|
27
|
+
- lib/user.rb
|
28
|
+
- public/users.json
|
29
|
+
homepage:
|
30
|
+
licenses: []
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
- public
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubygems_version: 3.1.2
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Spotify Rec generates track recommendations based upon a user defined list
|
52
|
+
of items
|
53
|
+
test_files: []
|