spotify web setup is done

This commit is contained in:
2025-06-22 10:01:32 +02:00
parent b368a750fe
commit 5eaad428ff
16 changed files with 365 additions and 15 deletions

View File

@@ -0,0 +1,28 @@
class CreateSpotifyActivity
attr_accessor :response, :user
def initialize(user, spotify_response)
@response = spotify_response
@user = user
end
# rubocop:disable Metrics/AbcSize
def perform
response['items'].reverse.each do |play|
activity = new_activity
activity.item_ref = play['track']['id']
artist_names = play['track']['artists'].map { |a| a['name'] }
artists = artist_names.join(', ')
title = "#{artists} - #{play['track']['name']}"
activity.item_title = title
activity.item_length = play['track']['duration'].to_s
activity.started_at = DateTime.parse(play['played_at'])
activity.save
end
end
# rubocop:enable Metrics/AbcSize
def new_activity
Activity.new(user: user, platform: 'spotify')
end
end

View File

@@ -0,0 +1,20 @@
class InitSpotifyLogin
def self.create_new_login
spotify = Spotify.new
puts 'Enter email:'
email = $stdin.gets.chomp
user = User.find_or_create_by!(email: email)
puts "\nopen in browser:"
puts spotify.auth_redirect_url.to_s
puts "\ncopy url from browser and paste: "
code_url = $stdin.gets.chomp
puts 'parsing...'
response = spotify.auth_code_authorize_url(code_url)
Login.find_or_create_for_response!(user, response)
puts 'login created successful'
end
end

View File

@@ -0,0 +1,53 @@
class LoadPlays
attr_reader :user
def initialize(user)
@user = user
end
def perform
user.logins.alive.each do |login|
if login.spotify?
update_spotify(login)
elsif login.netflix?
update_netflix(login)
else
puts "login #{login.id}, #{login.type} unknown"
end
end
end
def update_spotify(login)
client = Spotify.new(login)
last_spotify_activity_at = latest_activity_iso(login.platform)
new_activities = client.load_since(last_spotify_activity_at)
create_action = CreateSpotifyActivity.new(user, new_activities)
create_action.perform
rescue StandardError => e
Rails.logger.error(e)
LogLoadFailedWorker.perform_async(login.id)
end
def update_netflix(login)
client = Netflix.new(login)
latest_netflix_activity = latest_activity(login.platform)
new_activites = client.viewingactivity
create_action = CreateNetflixActivity.new(user, latest_netflix_activity, new_activites)
create_action.perform
rescue StandardError => e
Rails.logger.error(e)
LogLoadFailedWorker.perform_async(login.id)
# Rails.logger.info("trying to update shakti")
# ReloadShaktiPath.new.perform
end
def latest_activity(platform)
latest = Activity.where(user: user, platform: platform).order(started_at: :desc).limit(1).last
latest.started_at&.to_datetime if latest.present?
end
def latest_activity_iso(platform)
latest = latest_activity(platform)
latest.strftime('%Q') if latest.present?
end
end

View File

@@ -0,0 +1,113 @@
require "rest-client"
require "base64"
require "json"
class SpotifyClient
SPOTIFY_REDIRECT_URI = "#{ENV['SPOTIFY_REDIRECT_URI']}/spotify_callback".freeze
SCOPE = "user-read-recently-played".freeze
BASE_URL = "https://api.spotify.com/v1/".freeze
attr_reader :login
def initialize(login = nil)
@login = login
end
def load_since(load_since)
params = {
"limit" => "50"
}
params["after"] = load_since unless load_since.nil?
get("me/player/recently-played", params)
end
def token_response_from_code(code)
auth_code_authorize(code)
end
def auth_redirect_url
"https://accounts.spotify.com/authorize?" \
"response_type=code&" \
"client_id=#{ENV['SPOTIFY_CLIENT_ID']}&" \
"scope=#{SCOPE}&" \
"redirect_uri=#{CGI.escape(SPOTIFY_REDIRECT_URI)}"
end
private
def get(path, params = {})
refresh_token!
begin
call(path, params)
rescue RestClient::ExceptionWithResponse => e
if e.response.code == 401
puts "token expired, forcing refresh"
refresh_token!(force: true)
begin
call(path, params)
rescue RestClient::ExceptionWithResponse
puts "token dead"
LogLoadFailedWorker.perform_async(login.id)
end
end
end
end
def call(path, params)
headers = { "Authorization" => "Bearer #{login.credentials['access_token']}" }
headers[:params] = params if params.present?
JSON.parse RestClient.get("#{BASE_URL}#{path}", headers)
end
def refresh_token!(force: false)
return unless login.about_to_expire? || force
request_body = {
'refresh_token': login.credentials["refresh_token"],
'grant_type': "refresh_token"
}
begin
auth_response = RestClient.post(
"https://accounts.spotify.com/api/token",
request_body,
client_auth_headers
)
new_credentials = JSON.parse auth_response
login.update_access_token!(new_credentials)
rescue RestClient::ExceptionWithResponse => e
login.update(dead_since: Time.new)
raise e
end
end
def client_auth_headers
auth_plain = "#{ENV['SPOTIFY_CLIENT_ID']}:#{ENV['SPOTIFY_CLIENT_SECRET']}"
auth_string = Base64.strict_encode64 auth_plain
{
'Authorization': "Basic #{auth_string}"
}
end
def auth_code_authorize(code)
request_body = {
'code': code,
'redirect_uri': SPOTIFY_REDIRECT_URI,
'grant_type': "authorization_code"
}
begin
auth_response = RestClient.post(
"https://accounts.spotify.com/api/token",
request_body,
client_auth_headers
)
rescue RestClient::ExceptionWithResponse => e
raise e
end
raise StandardError.new("Authorization failed", auth_response) unless auth_response.code == 200
JSON.parse auth_response.body
end
end