spotify_rec 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/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: []
|