-
Notifications
You must be signed in to change notification settings - Fork 2
/
spotify.rb
165 lines (136 loc) · 5.4 KB
/
spotify.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# frozen_string_literal: true
require 'excon'
require 'rspotify'
require 'json'
# require "pry"
require_relative './db'
module SonosPartyMode
class Spotify
attr_accessor :user_id, :queued_songs, :past_songs
def initialize(user_id:, authorization_code: nil, redirect_uri: nil)
self.user_id = user_id
new_auth!(authorization_code: authorization_code, redirect_uri: redirect_uri) if authorization_code
return if database_row.nil? # this is the case if a user didn't finish onboarding
self.queued_songs = []
self.past_songs = []
end
def spotify_user
return nil if database_row.nil?
RSpotify::User.new(JSON.parse(database_row.fetch(:options)))
end
def database_row
query = Db.spotify_tokens.where(user_id: user_id)
return nil if query.empty?
return query.first
end
def party_playlist
return @_playlist if @_playlist
# Find or create the Party playlist
playlist_id = database_row.fetch(:playlist_id)
unless playlist_id
puts "Creating new playlist for user id #{user_id}"
playlist = spotify_user.create_playlist!("#{user_id} auxcord.org - Don't Delete")
playlist_id = playlist.id
prepare_welcome_playlist_song!(playlist)
puts "Finished creating playlist with id #{playlist_id}"
# Verify that the playlist was created and contains one song
raise 'Playlist was not created' if playlist.tracks.count == 0
# Remember the Spotify playlist ID
Db.spotify_tokens.where(user_id: user_id).update(playlist_id: playlist_id) # use full query syntax
end
return (@_playlist = RSpotify::Playlist.find(spotify_user.id, playlist_id))
end
# Add a welcome song to the playlist, so Sonos can handle the playlist
# Sonos app doesn't handle empty playlists well
def prepare_welcome_playlist_song!(playlist)
return if playlist.tracks.count.positive?
hello_there_song = RSpotify::Track.search('Hello there dillon francis').first
playlist.add_tracks!([hello_there_song])
end
def search_for_song(name)
return RSpotify::Track.search(name)
end
# Search for a specific Spotify song using the Spotify ID, including a local cache
def find_song(song_id)
return nil if song_id.to_s.length == 0
song_id.gsub!('spotify:track:', '')
@_song_cache ||= {}
return @_song_cache[song_id] if @_song_cache[song_id]
@_song_cache[song_id] = RSpotify::Track.find(song_id)
rescue => ex
puts ex
puts ex.backtrace.join("\n")
puts "Using song_id #{song_id}"
nil
end
# This method will add songs to the queue (playlist) on Spotify, but not yet add it to the Sonos queue
def add_song_to_queue(song)
queued_songs << song
end
# Actually send all songs wished for to the Sonos queue
def add_next_song_to_sonos_queue!(sonos)
# First, clear the Spotify playlist, in case there was anything left there
party_playlist.remove_tracks!(party_playlist.tracks)
next_song = queued_songs.shift
if next_song.nil?
puts 'No more auxcord songs in queue...'
return false
end
past_songs << next_song
party_playlist.add_tracks!([next_song])
# Get the Sonos ID of the favorite playlist
fav_id = sonos.ensure_playlist_in_favorites(party_playlist.id)
# Queue the one song from that playlist into the Sonos Queue
puts "Queueing #{next_song.name} by #{next_song.artists.first.name} to Sonos"
sonos.client_control_request(
"/groups/#{sonos.group_to_use}/favorites",
method: :post,
body: {
favoriteId: fav_id.fetch('id'),
action: 'INSERT_NEXT'
}
)
party_playlist.remove_tracks!([next_song])
return true
end
def self.permission_scope
return %w[
playlist-read-private
playlist-modify-public
user-library-modify
].join(' ')
end
def new_auth!(authorization_code:, redirect_uri:)
auth_string = Base64.strict_encode64("#{ENV.fetch('SPOTIFY_CLIENT_ID')}:#{ENV.fetch('SPOTIFY_CLIENT_SECRET')}")
auth_response = Excon.post(
'https://accounts.spotify.com/api/token',
body: URI.encode_www_form({
'grant_type' => 'authorization_code',
'redirect_uri' => redirect_uri,
'code' => authorization_code
}),
headers: {
'Authorization' => "Basic #{auth_string}",
'Content-Type' => 'application/x-www-form-urlencoded'
}
)
parsed_credentials = JSON.parse(auth_response.body)
# Manually re-name key, via https://github.com/guilhermesad/rspotify/issues/90#issuecomment-519603961
parsed_credentials['token'] = parsed_credentials['access_token']
info_response = Excon.get('https://api.spotify.com/v1/me',
headers: {
'Authorization' => "Bearer #{parsed_credentials.fetch('access_token')}"
})
info_parsed = JSON.parse(info_response.body)
options = {
'credentials' => parsed_credentials,
'info' => info_parsed
}
RSpotify::User.new(options)
Db.spotify_tokens.insert(
user_id: user_id,
options: JSON.pretty_generate(options.to_hash)
)
end
end
end