subcl 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/subcl +4 -124
- data/bin/subcl-api +22 -0
- data/lib/subcl.rb +14 -0
- data/lib/subcl/configs.rb +61 -0
- data/lib/subcl/notify.rb +66 -0
- data/lib/subcl/picker.rb +63 -0
- data/lib/subcl/player.rb +54 -0
- data/lib/subcl/runner.rb +154 -0
- data/lib/subcl/song.rb +14 -0
- data/lib/subcl/subcl.rb +146 -0
- data/lib/subcl/subcl_error.rb +1 -0
- data/lib/subcl/subsonic_api.rb +239 -0
- metadata +57 -22
- data/lib/Configs.rb +0 -67
- data/lib/Mpc.rb +0 -68
- data/lib/Notify.rb +0 -66
- data/lib/Picker.rb +0 -63
- data/lib/Song.rb +0 -14
- data/lib/Subcl.rb +0 -159
- data/lib/Subsonic.rb +0 -297
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: cce8d7f636cf3a5898c7ce68aa2287a8dcc6673d
         | 
| 4 | 
            +
              data.tar.gz: 1df467a85ba4ab604668844a359704200ee4c88c
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: adae72dbd6ab95c0501234706cdeceeb41fece5aacabf5847dd020c060e4d9a3f8cc36010ad34f70bd6c98bbe818b813f509711f98c2d7fd5bd7258ae2064513
         | 
| 7 | 
            +
              data.tar.gz: 8a5219d0d537d6d8450de8518d334084ddcea5be42709c8db0e4871db4bd468fb2bf22c0af3c430b1231176cbad23b46e18b75e674e2c0ec1a150278eb94da8a
         | 
    
        data/bin/subcl
    CHANGED
    
    | @@ -1,132 +1,12 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
            require 'optparse'
         | 
| 3 | 
            +
            require 'subcl'
         | 
| 5 4 |  | 
| 6 5 | 
             
            #don't throw a huge stacktrace
         | 
| 7 6 | 
             
            trap("INT") {
         | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 7 | 
            +
              puts "\n"
         | 
| 8 | 
            +
              exit
         | 
| 10 9 | 
             
            }
         | 
| 11 10 |  | 
| 12 | 
            -
             | 
| 11 | 
            +
            Runner.new.run ARGV
         | 
| 13 12 |  | 
| 14 | 
            -
            if File.exist?('debug')
         | 
| 15 | 
            -
            	puts "DEBUGGING"
         | 
| 16 | 
            -
            	options[:debug] = true
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            #no idea how to get this variable from outside, so we'll just set it in the loop
         | 
| 20 | 
            -
            usage = nil
         | 
| 21 | 
            -
            OptionParser.new do |opts|
         | 
| 22 | 
            -
            	opts.banner = "Usage: subcl [options] command"
         | 
| 23 | 
            -
            	opts.separator %{
         | 
| 24 | 
            -
            Commands
         | 
| 25 | 
            -
              list to terminal
         | 
| 26 | 
            -
                  search[-song|-album|-artist] <pattern>
         | 
| 27 | 
            -
                  ss|sl|sr <pattern>
         | 
| 28 | 
            -
              clear queue and immediately start playing
         | 
| 29 | 
            -
                  play[-song|-album|-artist|-playlist] <pattern>
         | 
| 30 | 
            -
                  ps|pl|pr|pp <pattern>
         | 
| 31 | 
            -
              clear queue and immediately start playing random songs
         | 
| 32 | 
            -
            			play-random <count, default 10>
         | 
| 33 | 
            -
            			r <count, default 10>
         | 
| 34 | 
            -
              add to end of queue
         | 
| 35 | 
            -
                  queue-last[-song|-album|-artist|-playlist] <pattern>
         | 
| 36 | 
            -
                  ls|ll|lr|lp <pattern>
         | 
| 37 | 
            -
              add after the current song
         | 
| 38 | 
            -
                  queue-next[-song|-album|-artist|-playlist] <pattern>
         | 
| 39 | 
            -
                  ns|nl|nr|np <pattern>
         | 
| 40 | 
            -
              albumart-url [size] - print url of albumart to terminal, 
         | 
| 41 | 
            -
                  optionally with a specified image size
         | 
| 42 | 
            -
             | 
| 43 | 
            -
            Options }
         | 
| 44 | 
            -
             | 
| 45 | 
            -
            	usage = opts
         | 
| 46 | 
            -
             | 
| 47 | 
            -
            	opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
         | 
| 48 | 
            -
            		options[:verbose] = v
         | 
| 49 | 
            -
            	end
         | 
| 50 | 
            -
            	opts.on('-1', '--use-first', 'On multiple matches, use the first match instead of asking interactively') do
         | 
| 51 | 
            -
            		options[:interactive] = false
         | 
| 52 | 
            -
            	end
         | 
| 53 | 
            -
            	opts.on('-s', '--shuffle', "Shuffle playlist before queueing") do
         | 
| 54 | 
            -
            		options[:shuffle] = true
         | 
| 55 | 
            -
            	end
         | 
| 56 | 
            -
            	opts.on('-c', '--current', 'Use info currently playing song instead of commandline argument') do
         | 
| 57 | 
            -
            		options[:current] = true
         | 
| 58 | 
            -
            	end
         | 
| 59 | 
            -
            	opts.on('-h', '--help', 'Display this screen') do
         | 
| 60 | 
            -
            		puts opts
         | 
| 61 | 
            -
            		exit
         | 
| 62 | 
            -
            	end
         | 
| 63 | 
            -
            	opts.on("--version", "Print version information") do
         | 
| 64 | 
            -
            		puts Configs.new.app_version
         | 
| 65 | 
            -
            		exit
         | 
| 66 | 
            -
            	end
         | 
| 67 | 
            -
             | 
| 68 | 
            -
            end.parse!
         | 
| 69 | 
            -
             | 
| 70 | 
            -
            unless ARGV.size >= 1
         | 
| 71 | 
            -
            	puts usage
         | 
| 72 | 
            -
            	exit
         | 
| 73 | 
            -
            end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
            unless system('tty -s')
         | 
| 76 | 
            -
            	#not running in a tty, so no use for interactivity
         | 
| 77 | 
            -
            	options[:tty] = false
         | 
| 78 | 
            -
            	options[:interactive] = false
         | 
| 79 | 
            -
            end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
            subcl = Subcl.new options
         | 
| 82 | 
            -
             | 
| 83 | 
            -
            arg = ARGV[1,ARGV.length-1].join(" ") #put rest of args together so no quotes are required
         | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
            case ARGV[0].downcase
         | 
| 87 | 
            -
            when /^play-song$|^ps$/
         | 
| 88 | 
            -
            	subcl.queue(arg, :song, {:play => true, :clear => true})
         | 
| 89 | 
            -
            when /^play-artist$|^pr$/
         | 
| 90 | 
            -
            	subcl.queue(arg, :artist, {:play => true, :clear => true})
         | 
| 91 | 
            -
            when /^play-album$|^pl$/
         | 
| 92 | 
            -
            	subcl.queue(arg, :album, {:play => true, :clear => true})
         | 
| 93 | 
            -
            when /^play-playlist$|^pp$/
         | 
| 94 | 
            -
            	subcl.queue(arg, :playlist, {:play => true, :clear => true})
         | 
| 95 | 
            -
            when /^play-random$|^r$/
         | 
| 96 | 
            -
            	subcl.queue(arg, :randomSong, {:play => true, :clear => true})
         | 
| 97 | 
            -
            when /^queue-next-song$|^ns$/
         | 
| 98 | 
            -
            	subcl.queue(arg, :song, {:insert => true})
         | 
| 99 | 
            -
            when /^queue-next-artist$|^nr$/
         | 
| 100 | 
            -
            	subcl.queue(arg, :artist, {:insert => true})
         | 
| 101 | 
            -
            when /^queue-next-album$|^nl$/
         | 
| 102 | 
            -
            	subcl.queue(arg, :album, {:insert => true})
         | 
| 103 | 
            -
            when /^queue-next-playlist$|^np$/
         | 
| 104 | 
            -
            	subcl.queue(arg, :playlist, {:insert => true})
         | 
| 105 | 
            -
            when /^queue-last-song$|^ls$/
         | 
| 106 | 
            -
            	subcl.queue(arg, :song)
         | 
| 107 | 
            -
            when /^queue-last-artist$|^lr$/
         | 
| 108 | 
            -
            	subcl.queue(arg, :artist)
         | 
| 109 | 
            -
            when /^queue-last-album$|^ll$/
         | 
| 110 | 
            -
            	subcl.queue(arg, :album)
         | 
| 111 | 
            -
            when /^queue-last-playlist$|^lp$/
         | 
| 112 | 
            -
            	subcl.queue(arg, :playlist)
         | 
| 113 | 
            -
            when /^search-song$|^ss$/
         | 
| 114 | 
            -
            	subcl.searchSong(arg)
         | 
| 115 | 
            -
            when /^search-artist$|^sr$/
         | 
| 116 | 
            -
            	subcl.searchArtist(arg)
         | 
| 117 | 
            -
            when /^search-album$|^sl$/
         | 
| 118 | 
            -
            	subcl.searchAlbum(arg)
         | 
| 119 | 
            -
            when "albumart-url"
         | 
| 120 | 
            -
            	arg = nil if arg.empty?
         | 
| 121 | 
            -
            	puts subcl.albumartUrl(arg)
         | 
| 122 | 
            -
            when /^album-list$|^al$/
         | 
| 123 | 
            -
            	subcl.subsonic.albumlist
         | 
| 124 | 
            -
            when "test-notify"
         | 
| 125 | 
            -
            	subcl.testNotify
         | 
| 126 | 
            -
            else
         | 
| 127 | 
            -
            	if options[:tty] then
         | 
| 128 | 
            -
            		puts usage
         | 
| 129 | 
            -
            	else
         | 
| 130 | 
            -
            		subcl.notifier.notify "Unrecognized command"
         | 
| 131 | 
            -
            	end
         | 
| 132 | 
            -
            end
         | 
    
        data/bin/subcl-api
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'subcl'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            if ARGV.empty?
         | 
| 6 | 
            +
              puts "Usage: subcl-api <method> [opt1=val1 [opt2=val2]...]"
         | 
| 7 | 
            +
              exit 1
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            api = SubsonicAPI.new(Configs.new)
         | 
| 11 | 
            +
            method = ARGV.shift
         | 
| 12 | 
            +
            method += ".view" unless method.end_with? ".view"
         | 
| 13 | 
            +
            params = Hash[ARGV.collect { |arg| arg.split('=') }]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            LOGGER.level = Logger::INFO
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            begin
         | 
| 18 | 
            +
              puts api.query(method, params)
         | 
| 19 | 
            +
            rescue SubclError => e
         | 
| 20 | 
            +
              $stderr.puts e.message
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
    
        data/lib/subcl.rb
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            require 'subcl/configs'
         | 
| 2 | 
            +
            require 'subcl/player'
         | 
| 3 | 
            +
            require 'subcl/notify'
         | 
| 4 | 
            +
            require 'subcl/picker'
         | 
| 5 | 
            +
            require 'subcl/runner'
         | 
| 6 | 
            +
            require 'subcl/song'
         | 
| 7 | 
            +
            require 'subcl/subcl'
         | 
| 8 | 
            +
            require 'subcl/subcl_error'
         | 
| 9 | 
            +
            require 'subcl/subsonic_api'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            require 'logger'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            LOGGER = Logger.new(STDERR)
         | 
| 14 | 
            +
            LOGGER.level = Logger::INFO
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            class Configs
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              attr_accessor :configs
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              REQUIRED_SETTINGS = %i{ server username password }
         | 
| 6 | 
            +
              OPTIONAL_SETTINGS = %i{ max_search_results notify_method random_song_count }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def initialize(file = '~/.subcl')
         | 
| 9 | 
            +
                @configs = {
         | 
| 10 | 
            +
                  :notifyMethod => "auto",
         | 
| 11 | 
            +
                }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                @filename = File.expand_path(file)
         | 
| 14 | 
            +
                unless File.file?(@filename)
         | 
| 15 | 
            +
                  raise "Config file not found"
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                read_configs
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def read_configs
         | 
| 22 | 
            +
                settings = REQUIRED_SETTINGS + OPTIONAL_SETTINGS
         | 
| 23 | 
            +
                open(@filename).each_line do |line|
         | 
| 24 | 
            +
                  next if line.start_with? '#'
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  key, value = line.split(' ')
         | 
| 27 | 
            +
                  key = key.to_sym
         | 
| 28 | 
            +
                  if settings.include? key
         | 
| 29 | 
            +
                    @configs[key] = value
         | 
| 30 | 
            +
                  else
         | 
| 31 | 
            +
                    LOGGER.warn { "Unknown setting: '#{key}'" }
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                REQUIRED_SETTINGS.each do |setting|
         | 
| 36 | 
            +
                  if @configs[setting].nil?
         | 
| 37 | 
            +
                    raise "Missing setting '#{setting}'"
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def [](key)
         | 
| 43 | 
            +
                raise "Undefined setting #{key}" unless @configs.has_key? key
         | 
| 44 | 
            +
                @configs[key]
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def []=(key, val)
         | 
| 48 | 
            +
                settings = REQUIRED_SETTINGS + OPTIONAL_SETTINGS
         | 
| 49 | 
            +
                settings.each do |name|
         | 
| 50 | 
            +
                  if key == name
         | 
| 51 | 
            +
                    @configs[key] = val
         | 
| 52 | 
            +
                    return
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
                raise "Undefined setting #{key}"
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def to_hash
         | 
| 59 | 
            +
                @configs
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
    
        data/lib/subcl/notify.rb
    ADDED
    
    | @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            class Notify
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              SupportedMethods = %w{notify-send growlnotify awesome-client}
         | 
| 4 | 
            +
              Icon = File.dirname(__FILE__) + "/../../share/icon.png"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def initialize(notifyMethod)
         | 
| 7 | 
            +
                @method = nil
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                case notifyMethod
         | 
| 10 | 
            +
                when nil
         | 
| 11 | 
            +
                  #do nothing
         | 
| 12 | 
            +
                when "none"
         | 
| 13 | 
            +
                  #do nothing
         | 
| 14 | 
            +
                when "auto"
         | 
| 15 | 
            +
                  #auto detect notifier lib
         | 
| 16 | 
            +
                  SupportedMethods.each do |method|
         | 
| 17 | 
            +
                    @binary = get_binary(method)
         | 
| 18 | 
            +
                    unless @binary.nil?
         | 
| 19 | 
            +
                      @method = method
         | 
| 20 | 
            +
                      break
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                else
         | 
| 24 | 
            +
                  #use specified binary
         | 
| 25 | 
            +
                  unless SupportedMethods.include? notifyMethod
         | 
| 26 | 
            +
                    raise ArgumentError, "Notification method #{notifyMethod} is not supported"
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                  @binary = get_binary(notifyMethod)
         | 
| 29 | 
            +
                  @method = notifyMethod
         | 
| 30 | 
            +
                  raise ArgumentError, "No binary found for #{notifyMethod} in PATH" if @binary.nil?
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def get_binary(name)
         | 
| 35 | 
            +
                binary = `which #{name}`.chomp
         | 
| 36 | 
            +
                if $?.success?
         | 
| 37 | 
            +
                  binary
         | 
| 38 | 
            +
                else
         | 
| 39 | 
            +
                  nil
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def notify(message)
         | 
| 44 | 
            +
                case @method
         | 
| 45 | 
            +
                when nil
         | 
| 46 | 
            +
                  #great, do nothing
         | 
| 47 | 
            +
                when "notify-send"
         | 
| 48 | 
            +
                  system("notify-send --icon #{Icon} --urgency critical Subcl '#{message}'")
         | 
| 49 | 
            +
                when "growlnotify"
         | 
| 50 | 
            +
                  system("growlnotify --image #{Icon} --title Subcl --message '#{message}'")
         | 
| 51 | 
            +
                when "awesome-client"
         | 
| 52 | 
            +
                  naughtyCmd = %Q{
         | 
| 53 | 
            +
                    naughty.notify({
         | 
| 54 | 
            +
                      title='subcl',
         | 
| 55 | 
            +
                      text='#{message}',
         | 
| 56 | 
            +
                      icon='#{Icon}',
         | 
| 57 | 
            +
                      timeout = 10
         | 
| 58 | 
            +
                    })
         | 
| 59 | 
            +
                  }
         | 
| 60 | 
            +
                  naughtyCmd.gsub! "\n" " "
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  system(%Q{echo "#{naughtyCmd}" | awesome-client})
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| 66 | 
            +
             | 
    
        data/lib/subcl/picker.rb
    ADDED
    
    | @@ -0,0 +1,63 @@ | |
| 1 | 
            +
            class Picker
         | 
| 2 | 
            +
              def initialize(ary)
         | 
| 3 | 
            +
                @available = ary
         | 
| 4 | 
            +
                if ary.empty? then
         | 
| 5 | 
            +
                  raise ArgumentError, "Cannot initialize Picker with an empty array!"
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def pick
         | 
| 10 | 
            +
                choices = {}
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                i = 1
         | 
| 13 | 
            +
                @available.each do |elem|
         | 
| 14 | 
            +
                  choices[i] = elem
         | 
| 15 | 
            +
                  $stderr.print "[#{i}] "
         | 
| 16 | 
            +
                  yield(elem)
         | 
| 17 | 
            +
                  i = i + 1
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
                begin
         | 
| 22 | 
            +
                  picks = []
         | 
| 23 | 
            +
                  valid = true
         | 
| 24 | 
            +
                  $stderr.print "Pick any: "
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  choice = $stdin.gets
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  return @available if choice.chomp == 'all'
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  choice.split(/[ ,]+/).each do |part|
         | 
| 31 | 
            +
                    possibleRange = part.split(/\.\.|-/)
         | 
| 32 | 
            +
                    if possibleRange.length == 2
         | 
| 33 | 
            +
                      start = possibleRange[0].to_i
         | 
| 34 | 
            +
                      stop = possibleRange[1].to_i
         | 
| 35 | 
            +
                      [start, stop].each do |num|
         | 
| 36 | 
            +
                        valid = validate(num)
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                      (start..stop).each do |num|
         | 
| 39 | 
            +
                        picks << choices[num]
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    elsif validate(num = part.to_i)
         | 
| 43 | 
            +
                      picks << choices[num]
         | 
| 44 | 
            +
                    else
         | 
| 45 | 
            +
                      valid == false
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end while !valid
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                return picks
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              def validate(pickNum)
         | 
| 54 | 
            +
                #no -1, we start filling choices{} at 1
         | 
| 55 | 
            +
                if pickNum > 0 and pickNum <= @available.length
         | 
| 56 | 
            +
                  true
         | 
| 57 | 
            +
                else
         | 
| 58 | 
            +
                  $stderr.puts "Invalid pick. Try again."
         | 
| 59 | 
            +
                  false
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            end
         | 
    
        data/lib/subcl/player.rb
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'ruby-mpd'
         | 
| 2 | 
            +
            require 'delegate'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Player < SimpleDelegator
         | 
| 5 | 
            +
              def initialize
         | 
| 6 | 
            +
                #TODO add configs for host/port/security
         | 
| 7 | 
            +
                @mpd = MPD.new
         | 
| 8 | 
            +
                @mpd.connect
         | 
| 9 | 
            +
                super(@mpd)
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              #insert: whether to add the song after the currently playing one
         | 
| 13 | 
            +
              #instead of the end of the queue
         | 
| 14 | 
            +
              def add(song, insert = false)
         | 
| 15 | 
            +
                unless song[:stream_url]
         | 
| 16 | 
            +
                  raise ArgumentError, "argument has no :stream_url!"
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                LOGGER.debug { "Adding #{song['title']}: #{song[:stream_url]}. Insert: #{insert}" }
         | 
| 19 | 
            +
                if insert then
         | 
| 20 | 
            +
                  pos = @mpd.current_song.pos + 1
         | 
| 21 | 
            +
                  @mpd.addid(song[:stream_url], pos)
         | 
| 22 | 
            +
                else
         | 
| 23 | 
            +
                  @mpd.add(song[:stream_url])
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              #stops the player and clears the playlist
         | 
| 28 | 
            +
              def clearstop
         | 
| 29 | 
            +
                @mpd.stop
         | 
| 30 | 
            +
                @mpd.clear
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              # if song has been playing for more than 4 seconds, rewind it to the start
         | 
| 34 | 
            +
              # otherwise go to the previous song
         | 
| 35 | 
            +
              def rewind
         | 
| 36 | 
            +
                if @mpd.status[:elapsed] > 4
         | 
| 37 | 
            +
                  @mpd.seek 0
         | 
| 38 | 
            +
                else
         | 
| 39 | 
            +
                  @mpd.previous
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              # if mpd is playing, pause it. Otherwise resume playback
         | 
| 44 | 
            +
              def toggle
         | 
| 45 | 
            +
                @mpd.pause = @mpd.playing? ? 1 : 0
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def pause
         | 
| 49 | 
            +
                @mpd.pause = 1
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              #TODO: might create a wrapper for current_song that makes API calls for artist, album, etc,
         | 
| 53 | 
            +
              # in case mpd is unable to decode the metadata
         | 
| 54 | 
            +
            end
         | 
    
        data/lib/subcl/runner.rb
    ADDED
    
    | @@ -0,0 +1,154 @@ | |
| 1 | 
            +
            require 'optparse'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Runner
         | 
| 4 | 
            +
              def initialize(options = {})
         | 
| 5 | 
            +
                @options = {
         | 
| 6 | 
            +
                  :tty => true,
         | 
| 7 | 
            +
                  :out_stream => STDOUT,
         | 
| 8 | 
            +
                  :err_stream => STDERR,
         | 
| 9 | 
            +
                  :mock_player => nil,
         | 
| 10 | 
            +
                  :mock_api => nil
         | 
| 11 | 
            +
                }.merge! options
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                #TODO refactor this away
         | 
| 14 | 
            +
                if File.exist?('debug')
         | 
| 15 | 
            +
                  puts "DEBUGGING"
         | 
| 16 | 
            +
                  @options[:debug] = true
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def parse_options! args
         | 
| 21 | 
            +
                OptionParser.new do |opts|
         | 
| 22 | 
            +
                  opts.banner = "Usage: subcl [options] command"
         | 
| 23 | 
            +
                  opts.separator %{
         | 
| 24 | 
            +
                Queue Commands
         | 
| 25 | 
            +
                  clear queue and immediately start playing
         | 
| 26 | 
            +
                      play[-song|-album|-artist|-playlist] <pattern>
         | 
| 27 | 
            +
                      ps|pl|pr|pp <pattern>
         | 
| 28 | 
            +
                  clear queue and immediately start playing random songs
         | 
| 29 | 
            +
                      play-random <count, default 10>
         | 
| 30 | 
            +
                      r <count, default 10>
         | 
| 31 | 
            +
                  add to end of queue
         | 
| 32 | 
            +
                      queue-last[-song|-album|-artist|-playlist] <pattern>
         | 
| 33 | 
            +
                      ls|ll|lr|lp <pattern>
         | 
| 34 | 
            +
                  add after the current song
         | 
| 35 | 
            +
                      queue-next[-song|-album|-artist|-playlist] <pattern>
         | 
| 36 | 
            +
                      ns|nl|nr|np <pattern>
         | 
| 37 | 
            +
                  albumart-url [size] - print url of albumart to terminal,
         | 
| 38 | 
            +
                      optionally with a specified image size
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                Playback Commands
         | 
| 41 | 
            +
                  play
         | 
| 42 | 
            +
                  pause
         | 
| 43 | 
            +
                  toggle (play when pause, pause when played)
         | 
| 44 | 
            +
                  stop
         | 
| 45 | 
            +
                  next
         | 
| 46 | 
            +
                  previous
         | 
| 47 | 
            +
                  rewind (get to start of song, or previous song when at start)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                Options }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  @usage = opts
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
         | 
| 54 | 
            +
                    @options[:verbose] = v
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                  opts.on('-1', '--use-first', 'On multiple matches, use the first match instead of asking interactively') do
         | 
| 57 | 
            +
                    @options[:interactive] = false
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  opts.on('-s', '--shuffle', "Shuffle playlist before queueing") do
         | 
| 60 | 
            +
                    @options[:shuffle] = true
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                  opts.on('-c', '--current', 'Use info currently playing song instead of commandline argument') do
         | 
| 63 | 
            +
                    @options[:current] = true
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  opts.on('-h', '--help', 'Display this screen') do
         | 
| 66 | 
            +
                    out_stream.puts opts
         | 
| 67 | 
            +
                    exit
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                  opts.on("--version", "Print version information") do
         | 
| 70 | 
            +
                    out_stream.puts Configs.new[:app_version]
         | 
| 71 | 
            +
                    exit
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                end.parse! args
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def run(args)
         | 
| 78 | 
            +
                LOGGER.debug { "args = #{args}" }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                parse_options!(args)
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                LOGGER.debug { "args = #{args}" }
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                unless args.size >= 1
         | 
| 85 | 
            +
                  @options[:err_stream].puts @usage
         | 
| 86 | 
            +
                  exit 3
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                unless system('tty -s')
         | 
| 90 | 
            +
                  #not running in a tty, so no use for interactivity
         | 
| 91 | 
            +
                  @options[:tty] = false
         | 
| 92 | 
            +
                  @options[:interactive] = false
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                subcl = Subcl.new @options
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                arg = args[1,args.length-1].join(" ") #put rest of args together so no quotes are required
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                command = args[0].downcase
         | 
| 100 | 
            +
                case command
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                when /^play-song$|^ps$/
         | 
| 103 | 
            +
                  subcl.queue(arg, :song, {:play => true, :clear => true})
         | 
| 104 | 
            +
                when /^play-artist$|^pr$/
         | 
| 105 | 
            +
                  subcl.queue(arg, :artist, {:play => true, :clear => true})
         | 
| 106 | 
            +
                when /^play-album$|^pl$/
         | 
| 107 | 
            +
                  subcl.queue(arg, :album, {:play => true, :clear => true})
         | 
| 108 | 
            +
                when /^play-playlist$|^pp$/
         | 
| 109 | 
            +
                  subcl.queue(arg, :playlist, {:play => true, :clear => true})
         | 
| 110 | 
            +
                when /^play-random$|^r$/
         | 
| 111 | 
            +
                  subcl.queue(arg, :randomSong, {:play => true, :clear => true})
         | 
| 112 | 
            +
                when /^queue-next-song$|^ns$/
         | 
| 113 | 
            +
                  subcl.queue(arg, :song, {:insert => true})
         | 
| 114 | 
            +
                when /^queue-next-artist$|^nr$/
         | 
| 115 | 
            +
                  subcl.queue(arg, :artist, {:insert => true})
         | 
| 116 | 
            +
                when /^queue-next-album$|^nl$/
         | 
| 117 | 
            +
                  subcl.queue(arg, :album, {:insert => true})
         | 
| 118 | 
            +
                when /^queue-next-playlist$|^np$/
         | 
| 119 | 
            +
                  subcl.queue(arg, :playlist, {:insert => true})
         | 
| 120 | 
            +
                when /^queue-last-song$|^ls$/
         | 
| 121 | 
            +
                  subcl.queue(arg, :song)
         | 
| 122 | 
            +
                when /^queue-last-artist$|^lr$/
         | 
| 123 | 
            +
                  subcl.queue(arg, :artist)
         | 
| 124 | 
            +
                when /^queue-last-album$|^ll$/
         | 
| 125 | 
            +
                  subcl.queue(arg, :album)
         | 
| 126 | 
            +
                when /^queue-last-playlist$|^lp$/
         | 
| 127 | 
            +
                  subcl.queue(arg, :playlist)
         | 
| 128 | 
            +
                when "albumart-url"
         | 
| 129 | 
            +
                  arg = nil if arg.empty?
         | 
| 130 | 
            +
                  @options[:out_stream].puts subcl.albumart_url(arg)
         | 
| 131 | 
            +
                when /^album-list$|^al$/
         | 
| 132 | 
            +
                  subcl.albumlist
         | 
| 133 | 
            +
                when "test-notify"
         | 
| 134 | 
            +
                  subcl.testNotify
         | 
| 135 | 
            +
                else
         | 
| 136 | 
            +
                  begin
         | 
| 137 | 
            +
                    #pass through for player commands
         | 
| 138 | 
            +
                    subcl.send(command, [])
         | 
| 139 | 
            +
                  rescue NoMethodError
         | 
| 140 | 
            +
                    unknown(command)
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              def unknown(command)
         | 
| 146 | 
            +
                if @options[:tty] then
         | 
| 147 | 
            +
                  @options[:err_stream].puts "Unknown command '#{command}'"
         | 
| 148 | 
            +
                  @options[:err_stream].puts @usage
         | 
| 149 | 
            +
                else
         | 
| 150 | 
            +
                  subcl.notifier.notify "Unknown command '#{command}'"
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
                exit 3
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
            end
         |