Last active
August 29, 2015 14:02
-
-
Save ericboehs/7d4488cbe9e0b9961a06 to your computer and use it in GitHub Desktop.
Example ruby script to retrieve users playlists (including starred)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# encoding: utf-8 | |
# Gemfile: | |
# source "https://rubygems.org" | |
# | |
# gem 'spotify' | |
# gem 'pry' | |
require "bundler/setup" | |
require "spotify" | |
require "logger" | |
require "pry" | |
require "io/console" | |
# Kill main thread if any other thread dies. | |
Thread.abort_on_exception = true | |
# We use a logger to print some information on when things are happening. | |
$stderr.sync = true | |
$logger = Logger.new($stderr) | |
$logger.level = Logger::INFO | |
$logger.formatter = proc do |severity, datetime, progname, msg| | |
progname = if progname | |
" (#{progname}) " | |
else | |
" " | |
end | |
"\n[#{severity} @ #{datetime.strftime("%H:%M:%S")}]#{progname}#{msg}" | |
end | |
# | |
# Some utility. | |
# | |
module Support | |
module_function | |
DEFAULT_CONFIG = Spotify::SessionConfig.new({ | |
api_version: Spotify::API_VERSION.to_i, | |
application_key: File.binread("./spotify_appkey.key"), | |
cache_location: ".spotify/", | |
settings_location: ".spotify/", | |
user_agent: "spotify for ruby", | |
callbacks: Spotify::SessionCallbacks.new | |
}) | |
def logger | |
$logger | |
end | |
class << self | |
attr_accessor :silenced | |
end | |
# libspotify supports callbacks, but they are not useful for waiting on | |
# operations (how they fire can be strange at times, and sometimes they | |
# might not fire at all). As a result, polling is the way to go. | |
def poll(session, idle_time = 0.05) | |
until yield | |
print "." unless silenced | |
process_events(session) | |
sleep(idle_time) | |
end | |
end | |
# Process libspotify events once. | |
def process_events(session) | |
FFI::MemoryPointer.new(:int) do |ptr| | |
Spotify.session_process_events(session, ptr) | |
end | |
end | |
# Ask the user for input with a prompt explaining what kind of input. | |
def prompt(message, default = nil) | |
if default | |
print "\n#{message} [#{default}]: " | |
input = gets.chomp | |
if input.empty? | |
default | |
else | |
input | |
end | |
else | |
print "\n#{message}: " | |
gets.chomp | |
end | |
end | |
def initialize_spotify!(config = DEFAULT_CONFIG) | |
session = FFI::MemoryPointer.new(Spotify::Session) do |ptr| | |
Spotify.try(:session_create, config, ptr) | |
break Spotify::Session.new(ptr.read_pointer) | |
end | |
remembered_user_length = Spotify.session_remembered_user(session, nil, 0) | |
if remembered_user_length > 0 | |
username = FFI::MemoryPointer.new(:int, remembered_user_length + 1) do |username_ptr| | |
Spotify.session_remembered_user(session, username_ptr, username_ptr.size) | |
break username_ptr.read_string.force_encoding("UTF-8") | |
end | |
logger.info "Using remembered login for: #{username}." | |
Spotify.try(:session_relogin, session) | |
else | |
username = prompt("Spotify username, or Facebook e-mail") | |
password = $stdin.noecho { prompt("Spotify password, or Facebook password") } | |
logger.info "Attempting login with #{username}." | |
Spotify.try(:session_login, session, username, password, true, nil) | |
end | |
logger.info "Log in requested. Waiting forever until logged in." | |
Support.poll(session) { Spotify.session_connectionstate(session) == :logged_in } | |
at_exit do | |
logger.info "Logging out." | |
Spotify.session_logout(session) | |
Support.poll(session) { Spotify.session_connectionstate(session) != :logged_in } | |
end | |
session | |
end | |
end | |
# | |
# Main | |
# | |
session = Support.initialize_spotify! | |
username = Support.prompt("Please enter a username", "burgestrand") | |
# libspotify has no way of searching for users by name or e-mail, so | |
# this is the only way I know of to get a handle of a user from nothing, | |
# and probably won't work for users that do not have a classic Spotify | |
# username, i.e. users that only signed up through Facebook. | |
# | |
# If you can get a handle on a playlist created by that user, you'll be | |
# able to get the handle for them, but not otherwise! | |
# | |
# For users that signed up through Facebook only, I believe their Facebook | |
# user ID should work with this lookup. | |
user_link = "spotify:user:#{username}" | |
link = Spotify.link_create_from_string(user_link) | |
if link.null? | |
$logger.error "#{user_link} was apparently not parseable as a Spotify URI. Aborting." | |
abort | |
end | |
user = Spotify.link_as_user(link) | |
$logger.info "Attempting to load #{user_link}. Waiting forever until successful." | |
Support.poll(session) { Spotify.user_is_loaded(user) } | |
display_name = Spotify.user_display_name(user) | |
canonical_name = Spotify.user_canonical_name(user) | |
$logger.info "User loaded: #{display_name}." | |
$logger.info "Loading user playlists by loading their published container: #{canonical_name}." | |
container = Spotify.session_publishedcontainer_for_user_create(session, canonical_name) | |
$logger.info "Attempting to load container. Waiting forever until successful." | |
Support.poll(session) { Spotify.playlistcontainer_is_loaded(container) } | |
$logger.info "Container loaded. Loading playlists until no more are loaded for three tries!" | |
container_size = 0 | |
previous_container_size = 0 | |
break_counter = 0 | |
loop do | |
container_size = Spotify.playlistcontainer_num_playlists(container) | |
new_playlists = container_size - previous_container_size | |
previous_container_size = container_size | |
$logger.info "Loaded #{new_playlists} more playlists." | |
# If we have loaded no new playlists for 4 tries, we assume we are done. | |
if new_playlists == 0 | |
break_counter += 1 | |
if break_counter >= 4 | |
break | |
end | |
end | |
$logger.info "Loading…" | |
5.times do | |
Support.process_events(session) | |
sleep 0.2 | |
end | |
end | |
$logger.info "#{container_size} published playlists for #{display_name} found. Loading each playlists and printing it." | |
container_size.times do |index| | |
type = Spotify.playlistcontainer_playlist_type(container, index) | |
playlist = Spotify.playlistcontainer_playlist(container, index) | |
Support.poll(session) { Spotify.playlist_is_loaded(playlist) } | |
playlist_name = Spotify.playlist_name(playlist) | |
num_tracks = Spotify.playlist_num_tracks(playlist) | |
# Retrieving link for playlist: | |
playlist_link = Spotify.link_create_from_playlist(playlist) | |
link_string = if playlist_link.nil? | |
"(no link)" | |
else | |
# Spotify.link_as_string(playlist_link) | |
end | |
$logger.info " (#{type}) #{playlist_name}: #{link_string} (#{num_tracks} tracks)" | |
binding.pry | |
end | |
$logger.info "All done." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment