Skip to content

Instantly share code, notes, and snippets.

@youpy
Last active September 26, 2015 20:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save youpy/1156159 to your computer and use it in GitHub Desktop.
Save youpy/1156159 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
#
# for OSX only
#
# $ gem install launch-agent
# $ launchagent --daemon --env "RBENV_VERSION=2.0.0-p0" -- zsh -c "ruby /path/to/scrobbler.rb"
require 'rubygems'
require 'lastfm'
require 'soundcloud'
require 'pit'
require 'eventmachine-itunes'
require 'logger'
class VLogger < Logger
def initialize(*args)
super(*args)
Thread.start do
loop do
@logdev.dev.puts('')
sleep 5
end
end
end
end
$stdout.sync = true
$logger =
# VLogger.
Logger.
new(STDOUT)
class Faver < EventMachine::ITunesWatch
def on_play(info)
description = info['Description']
rating = info['Rating'].to_i
if rating > 0
if description && matched = description.match(/^http:\/\/soundcloud[^ ]+/)
favorite(matched[0])
end
end
end
def favorite(url)
$logger.info("(%s) favorite" % url)
soundcloud.put(api_base_url + '/me/favorites/%i' % track(url).id)
end
def track(url)
soundcloud.get(api_base_url + '/resolve.json?url=%s&client_id=%s' % [url, soundcloud.client_id])
end
def api_base_url
'http://api.soundcloud.com'
end
def config
@config ||= Pit.get("soundcloud.com", :require => {
"client_id" => "your client ID in soundcloud.com",
"client_secret" => "your client secret in soundcloud.com",
"username" => "your username in soundcloud.com",
"password" => "your password in soundcloud.com"
}
)
end
def soundcloud
@soundcloud ||=
Soundcloud.new(
:client_id => config['client_id'],
:client_secret => config['client_secret'],
:username => config['username'],
:password => config['password']
)
end
end
class Scrobbler < EventMachine::ITunesWatch
def cancel_timer
@scrobble_timer.cancel if @scrobble_timer
$logger.info 'cancel timer'
end
def on_pause(info)
clear_now_playing
cancel_timer
end
def on_stop(info)
clear_now_playing
cancel_timer
end
def on_play(info)
name = info['Name']
artist = info['Artist']
rating = info['Rating'].to_i
duration = info['Total Time'] / 1000
description = info['Description']
tag = nil
if name && artist
if start_playing?(artist, name)
cancel_timer
update_now_playing(artist, name, duration)
set_timer_if_needed(duration) do
scrobble(artist, name, duration)
end
end
remove_rating_tag(artist, name)
if rating > 0
love(artist, name, rating)
tag = (rating / 20).to_s
add_tag(artist, name, tag)
end
end
end
def scrobble(artist, name, duration)
lastfm.track.scrobble(
:artist => artist,
:track => name,
:timestamp => Time.now.to_i,
:duration => duration)
$logger.info("(%s / %s) scrobble" % [name, artist])
end
def clear_now_playing
@now_playing = nil
$logger.info 'clear now playing'
end
def update_now_playing(artist, name, duration)
lastfm.track.update_now_playing(
:artist => artist,
:track => name,
:duration => duration)
@now_playing = [artist, name].join('&&')
$logger.info("(%s / %s) update_now_playing" % [name, artist])
end
def start_playing?(artist, name)
@now_playing != [artist, name].join('&&')
end
def love(artist, name, rating)
if rating > 80
lastfm.track.love(
:artist => artist,
:track => name)
$logger.info("(%s / %s) love" % [name, artist])
else
lastfm.track.unlove(
:artist => artist,
:track => name)
$logger.info("(%s / %s) unlove" % [name, artist])
end
end
def add_tag(artist, name, tag)
lastfm.track.add_tags(
:artist => artist,
:track => name,
:tags => tag)
$logger.info("(%s / %s) add tag: %s" % [name, artist, tag])
end
def remove_rating_tag(artist, name)
tags = lastfm.track.get_tags(
:artist => artist,
:track => name) || []
tags.each do |t|
if t['name'] =~ /^\d+$/
lastfm.track.remove_tag(
:artist => artist,
:track => name,
:tag => t['name'])
$logger.info("(%s / %s) remove tag: %s" % [name, artist, t['name']])
end
end
end
# http://www.lastfm.jp/api/scrobbling#when-is-a-scrobble-a-scrobble
#
# The track must be longer than 30 seconds.
# And the track has been played for at least half its duration, or for 4 minutes (whichever occurs earlier.)
#
def set_timer_if_needed(duration, &callback)
return unless duration > 30
half_duration = duration / 2
four_mins = 60 * 4
timer_sec = half_duration > four_mins ? four_mins : half_duration
$logger.info 'add timer (fired after %i seconds)' % timer_sec
@scrobble_timer = EventMachine::Timer.new(timer_sec) do
callback.call
end
end
def lastfm
return @lastfm if @lastfm
config = Pit.get("last.fm", :require => {
"api_key" => "your api key in last.fm",
"api_secret" => "your api secret in last.fm"
})
lastfm = Lastfm.new(
config['api_key'],
config['api_secret'])
unless session = config['session']
token = lastfm.auth.get_token
auth('http://www.last.fm/api/auth/?api_key=' + config['api_key'] + '&token=' + token)
session = lastfm.auth.get_session(:token => token)['key']
Pit.set('last.fm', :data => {
"session" => session
}.merge(config))
puts "Session key was generated."
end
lastfm.session = session
@lastfm = lastfm
end
def auth(auth_url)
unless system('open', auth_url)
print 'open ' + auth_url + ' in your browser and '
end
print 'after authorization, push any key:'
result = STDIN.gets.chomp
puts
result
end
end
def main
EM.run do
[Scrobbler, Faver].each do |klass|
EM.watch_itunes(klass)
end
end
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment