source :rubygems
-gem 'nokogiri'
-gem 'multi_json'
-gem 'rdio'
+gem 'nokogiri', "~> 1.5.5"
+gem 'multi_json', "~> 1.3.6"
+gem 'rdio', "~> 0.1.0"
+gem "launchy", "~> 2.1.2"
GEM
remote: http://rubygems.org/
specs:
+ addressable (2.3.2)
json (1.7.5)
+ launchy (2.1.2)
+ addressable (~> 2.3)
multi_json (1.3.6)
nokogiri (1.5.5)
oauth (0.4.7)
ruby
DEPENDENCIES
- multi_json
- nokogiri
- rdio
+ launchy (~> 2.1.2)
+ multi_json (~> 1.3.6)
+ nokogiri (~> 1.5.5)
+ rdio (~> 0.1.0)
--- /dev/null
+#!/usr/bin/env ruby -w
+
+require 'uri'
+require 'net/http'
+require 'multi_json'
+require 'nokogiri'
+require 'open-uri'
+require 'launchy'
+
+# This shit causes a lot of warnings. Quick Hack.
+original_verbosity = $VERBOSE
+$VERBOSE = nil
+require 'rdio'
+$VERBOSE = original_verbosity
+
+class Lyricli
+
+ # TODO: Change the whole fucking thing
+ def initialize
+ @rdio_key = "sddac5t8akqrzh5b6kg53jfm"
+ @rdio_secret = "PRcB8TggFr"
+ @token_path = File.expand_path("~/.rdio_token")
+
+ #Expand the symlink and get the path
+ if File.symlink?(__FILE__) then
+ path = File.dirname(File.readlink(__FILE__))
+ else
+ path = File.dirname(__FILE__)
+ end
+
+ # Get the current rdio track
+ @rdio = init_rdio
+ rdio_track
+
+ #Get the current iTunes track
+ current = `osascript #{path}/current_song.scpt`
+ if current and not current.empty? then
+ current = current.split("<-SEP->")
+ @artist ||= current[0]
+ @song ||= current[1]
+ end
+ end
+
+ def init_rdio
+
+ if File.exists?(@token_path)
+ f = File.new(@token_path, "r")
+ begin
+ token = MultiJson.decode(f.read)
+ rescue
+ token = create_rdio_token
+ end
+ else
+ token = create_rdio_token
+ end
+
+ Rdio::SimpleRdio.new([@rdio_key, @rdio_secret], token)
+ end
+
+
+ def exit_with_error
+ abort "Usage: #{$0} artist song"
+ end
+
+ def get_lyrics
+
+ #Use the API to search
+ uri = URI("http://lyrics.wikia.com/api.php?artist=#{self.sanitize_param @artist}&song=#{self.sanitize_param @song}&fmt=realjson")
+ begin
+ res = Net::HTTP.get(uri)
+ res = MultiJson.decode(res)
+
+ #Get the actual lyrics url
+ doc = Nokogiri::HTML(open(res['url']))
+ node = doc.search(".lyricbox").first
+ rescue
+ abort "Lyrics not found :("
+ end
+
+ #Remove the rtMatcher nodes
+ node.search(".rtMatcher").each do |n|
+ n.remove
+ end
+
+ #Maintain new lines
+ node.search("br").each do |br|
+ br.replace "\n"
+ end
+
+ #Retrieve the lyrics
+ puts node.inner_text
+ end
+
+ def check_params
+ self.exit_with_error if @artist.nil? or @artist.empty?
+ self.exit_with_error if @song.nil? or @song.empty?
+ end
+
+ def sanitize_param(p)
+ URI.encode_www_form_component(p.gsub(/ /, "+")).gsub("%2B", "+")
+ end
+end
+
+
+lrc = Lyricli.new
+lrc.check_params
+lrc.get_lyrics
--- /dev/null
+module Lyricli
+ class Config
+
+ def initialize
+ @config_path = "~/.lyricli.conf"
+ @config = load_config
+ end
+
+ @@instance = Config.new
+
+ def self.instance
+ @@instance
+ end
+
+ def [](key)
+ @config[key]
+ end
+
+ def []=(key, value)
+ @config[key] = value
+ save_config
+ end
+
+ private_class_method :new
+
+ private
+
+ # TODO: Apart from this, load a default yml that will be used for this.
+ # And just extend everything from the user's config.
+ def load_config
+ path = File.expand_path(@config_path)
+ if File.exists?(path)
+ file = File.new(path, "r")
+ MultiJson.decode(file.read)
+ else
+ {}
+ end
+ end
+
+ def save_config
+ path = File.expand_path(@config_path)
+ file = File.new(path, "w")
+ file.print(MultiJson.encode(@config))
+ file.close
+ end
+ end
+end
--- /dev/null
+module Lyricli
+ module Sources
+ module Rdio
+
+ # Returns the name of the source, in snake_case
+ def self.name
+ "rdio"
+ end
+
+ # The enable method should run all of the tasks needed to validate
+ # the source. In the case of Rdio it has to authenticate with OAuth.
+ def self.enable
+ # Validation Code
+ @config = Lyricli::Config
+ unless @config[:rdio_auth_token] && !@config[:rdio_auth_token].empty?
+ create_auth_token
+ end
+
+ end
+
+ # Instantiates everything it needs to run.
+ def self.start
+ @rdio = Rdio::SimpleRdio.new([@config[:rdio_key], @config[:rdio_secret]], @config[:rdio_auth_token])
+ end
+
+ # The current_track method should return the name of the current
+ # artist and song.
+ # @return [Hash] A hash containing the current `:song` and `:artist`.
+ def self.current_track
+ u = @rdio.call('currentUser', {'extras' => 'lastSongPlayed'})
+ artist = u["result"]["lastSongPlayed"]["artist"]
+ song = u["result"]["lastSongPlayed"]["name"]
+ {artist: artist, song: song}
+ end
+
+ # The reset method resets any configurations it may have
+ def self.reset
+ # Reset Code
+ end
+
+ private
+
+ # Signs in to rdio with our credentials and requests access for a new auth
+ # token.
+ def create_auth_token
+ rdio = Rdio::SimpleRdio.new([@config])
+
+ # Request Authorization
+ puts "Follow this URL to authorize lyricli:"
+ auth_url = rdio.begin_authentication('oob')
+ puts auth_url
+ Launchy.open(auth_url)
+
+ # Request Code, Obtain Token
+ print "Please type the authorization code: "
+ auth_code = gets.chomp
+ token = rdio.complete_authentication(auth_code)
+
+ @config[:rdio_auth_token] = token
+ token
+ end
+
+ end
+ end
+end
--- /dev/null
+module Lyricli
+ module Util
+ def camelize(str)
+ str.split('_').map {|w| w.capitalize}.join
+ end
+
+ def parse_class(class_name)
+ klass = Module.const_get(class_name)
+ return klass if klass.is_a?(Class)
+ rescue NameError
+ return nil
+ end
+ end
+end
--- /dev/null
+module Lyricli
+ class SourceManager
+
+ include Lyricli::Util
+
+ def initialize
+ @enabled_sources = []
+ @config = Lyricli::Config
+ end
+
+ def enable(source_name)
+ if source_module = module_exists?(camelize(str))
+ source_module.enable
+ @config[:enabled_sources] << klass.name
+ else
+ raise Lyricli::EnableSourceException
+ end
+ end
+
+ def disable(source_name)
+ if source_module = module_exists?(camelize(str))
+ @config[:enabled_sources].delete(klass.name)
+ else
+ raise Lyricli::DisableSourceException
+ end
+ end
+
+ def reset(source_name)
+ if source_module = module_exists?(camelize(str))
+ source_module.reset
+ disable(source_name)
+ else
+ raise Lyricli::ResetSourceException
+ end
+ end
+
+ def start
+ @config[:enabled_sources].each do |source|
+ begin
+ source.start
+ rescue
+ fail "Source #{source.name} has failed to start. Please reset the source by running `#{$0} source reset #{source.name}.`"
+ end
+ end
+ end
+ end
+end