Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit 6a271d83a9996328a0979d1d6cebdafd556ac02f @meh committed Jun 27, 2012
3 README.md
@@ -0,0 +1,3 @@
+MPD - Ruby controller
+======================
+This is just a simple library to control [MPD](http://mpd.wikia.com/wiki/Music_Player_Daemon_Wiki).
8 bin/mpc.rb
@@ -0,0 +1,8 @@
+#! /usr/bin/env ruby
+require 'mpd'
+require 'optparse'
+
+options = {}
+
+OptionParser.new do |o|
+end.parse!
12 lib/mpd.rb
@@ -0,0 +1,12 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+require 'mpd/controller'
+require 'mpd/version'
80 lib/mpd/controller.rb
@@ -0,0 +1,80 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+require 'socket'
+
+require 'mpd/protocol'
+
+require 'mpd/controller/commands'
+require 'mpd/controller/toggle'
+require 'mpd/controller/player'
+
+module MPD
+
+# This is the main class to manage moc.
+#
+# The class also acts as a Socket if needed.
+class Controller
+ attr_reader :path, :host, :port, :version
+
+ def initialize (host = 'localhost', port = 6600)
+ @socket = File.exists?(host) ? UNIXSocket.new(host) : TCPSocket.new(host, port)
+ @version = @socket.readline.chomp.split(' ', 3).last
+
+ if unix?
+ @path = host
+ else
+ @host = host
+ @port = port
+ end
+ end
+
+ def unix?
+ @socket.is_a? UNIXSocket
+ end
+
+ def tcp?
+ @socket.is_a? TCPSocket
+ end
+
+ def respond_to_missing? (id, include_private = false)
+ @socket.respond_to? id, include_private
+ end
+
+ def method_missing (id, *args, &block)
+ if @socket.respond_to? id
+ return @socket.__send__ id, *args, &block
+ end
+
+ super
+ end
+
+ def commands (&block)
+ Commands.new(self, &block).send
+ end
+
+ def command (name, *args)
+ command = Protocol::Command.new(name, args)
+
+ @socket.puts command.to_s
+
+ Protocol::Response.read(self, command)
+ end
+
+ def toggle
+ Toggle.new(self)
+ end
+
+ def player
+ Player.new(self)
+ end
+end
+
+end
42 lib/mpd/controller/commands.rb
@@ -0,0 +1,42 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; class Controller
+
+class Commands < BasicObject
+ attr_reader :controller
+
+ def initialize (controller, &block)
+ @controller = controller
+ @commands = []
+
+ instance_exec &block if block
+ end
+
+ def method_missing (id, *args)
+ @commands << ::MPD::Protocol::Command.new(id, args)
+ end
+
+ def send
+ controller.puts ::MPD::Protocol::CommandList.new(@commands).to_s
+
+ result = []
+
+ @commands.each {|command|
+ result << ::MPD::Protocol::Response.read(controller, command)
+
+ break if result.last.is_a? ::MPD::Protocol::Error
+ } and controller.readline
+
+ result
+ end
+end
+
+end; end
75 lib/mpd/controller/player.rb
@@ -0,0 +1,75 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; class Controller
+
+class Player
+ attr_reader :controller
+
+ def initialize (controller)
+ @controller = controller
+ end
+
+ def play (what = nil)
+ if what.nil?
+ unpause
+ elsif what.is_a? Integer
+ controller.command :play, what
+ else
+ controller.command :play, what.to_sym
+ end
+
+ self
+ end
+
+ def pause
+ controller.command :pause, true
+
+ self
+ end
+
+ def unpause
+ controller.command :pause, false
+
+ self
+ end
+
+ def stop
+ controller.command :stop
+
+ self
+ end
+
+ def next
+ controller.command :next
+
+ self
+ end
+
+ def prev
+ controller.command :previous
+
+ self
+ end
+
+ def volume (volume)
+ controller.command :setvol, volume
+
+ self
+ end
+
+ def seek (second)
+ controller.command :seekcur, second
+
+ self
+ end
+end
+
+end; end
67 lib/mpd/controller/toggle.rb
@@ -0,0 +1,67 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; class Controller
+
+class Toggle
+ attr_reader :controller
+
+ def initialize (controller)
+ @controller = controller
+ end
+
+ def toggle (name)
+ controller.command(name, !on?(name))
+ end
+
+ def on (name)
+ unless on? name
+ controller.command(name, true)
+ end
+
+ self
+ end
+
+ def off (name)
+ if on? name
+ controller.command(name, false)
+ end
+
+ self
+ end
+
+ def on? (name)
+ result = controller.command(:status).to_hash[name]
+
+ return false if result != true && result != false
+
+ return result
+ end
+
+ %w[pause random consume repeat single].each {|name|
+ define_method name do
+ toggle name
+ end
+
+ define_method "#{name}!" do
+ on option_name
+ end
+
+ define_method "no_#{name}!" do
+ off option_name
+ end
+
+ define_method "#{name}?" do
+ on? option_name
+ end
+ }
+end
+
+end; end
15 lib/mpd/protocol.rb
@@ -0,0 +1,15 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+require 'stringio'
+
+require 'mpd/protocol/response'
+require 'mpd/protocol/command'
+require 'mpd/protocol/command_list'
50 lib/mpd/protocol/command.rb
@@ -0,0 +1,50 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; module Protocol
+
+class Command
+ attr_reader :name, :arguments
+
+ def initialize (name, *arguments)
+ @name = name.to_sym
+ @arguments = arguments.flatten.compact
+ end
+
+ def to_s
+ result = name.to_s
+
+ unless arguments.empty?
+ result << ' ' << arguments.map {|argument|
+ if argument.is_a? Range
+ if argument.end == -1
+ "#{argument.begin}:"
+ else
+ "#{argument.begin}:#{argument.end + (argument.exclude_end? ? 0 : 1)}"
+ end
+ elsif argument == true || argument == false
+ argument ? '1' : '0'
+ else
+ argument = argument.to_s
+
+ if argument.include?(' ') || argument.include?('"')
+ %{"#{argument.gsub '"', '\"'}"}
+ else
+ argument
+ end
+ end
+ }.join(' ')
+ end
+
+ result
+ end
+end
+
+end; end
48 lib/mpd/protocol/command_list.rb
@@ -0,0 +1,48 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; module Protocol
+
+class CommandList
+ def initialize (*commands)
+ @commands = commands.flatten.compact
+ end
+
+ def respond_to_missing? (id, include_private = false)
+ @commands.respond_to?(id)
+ end
+
+ def method_missing (id, *args, &block)
+ if @commands.respond_to? id
+ return @commands.__send__ id, *args, &block
+ end
+
+ super
+ end
+
+ def to_s
+ result = StringIO.new
+
+ result.puts 'command_list_ok_begin'
+ @commands.each {|command|
+ result.puts command.to_s
+ }
+ result.print 'command_list_end'
+
+ result.seek 0
+ result.read
+ end
+
+ def to_a
+ @commands
+ end
+end
+
+end; end
164 lib/mpd/protocol/response.rb
@@ -0,0 +1,164 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD; module Protocol
+
+class Response
+ class Data
+ attr_reader :name, :value
+
+ def initialize (name, value)
+ @name = name
+ @value = case name
+ when :artists, :albums, :songs, :uptime, :playtime, :db_playtime, :volume, :playlist, :playlistlength, :xfade
+ value.to_i
+
+ when :mixrampdb, :mixrampdelay
+ value == 'nan' ? Float::NAN : value.to_f
+
+ when :repeat, :random, :single, :consume
+ value != '0'
+
+ when :db_update
+ Time.at(value.to_i)
+
+ when :command, :state
+ value.to_sym
+
+ else value
+ end
+ end
+ end
+
+ def self.read (io, command)
+ result = []
+
+ while (line = io.readline) && !line.match(/^(list_)?OK(\s|$)/) && !line.start_with?('ACK ')
+ name, value = line.split ': ', 2
+ name = name.to_sym
+ value = value.chomp
+
+ result << Data.new(name, value)
+ end
+
+ type, message = line.split ' ', 2
+
+ if type == 'OK' || type == 'list_OK'
+ Ok.new(command, result, message)
+ else
+ Error.new(command, result, *Error.parse(message))
+ end
+ end
+
+ def self.parse (text)
+ read(StringIO.new(text))
+ end
+
+ include Enumerable
+
+ attr_reader :command
+
+ def initialize (command, data)
+ @command = command
+ @internal = data.freeze
+ end
+
+ def each
+ @internal.each {|data|
+ yield [data.name, data.value]
+ }
+
+ self
+ end
+
+ def to_a
+ @internal
+ end
+
+ def to_hash
+ return @hash if @hash
+
+ result = {}
+
+ each {|name, value|
+ if result[name]
+ if result[name].is_a? Array
+ result[name] << value
+ else
+ result[name] = [result[name], value]
+ end
+ else
+ result[name] = value
+ end
+ }
+
+ @hash = result.freeze
+ end
+end
+
+class Ok < Response
+ attr_reader :message
+
+ def initialize (command, data, message)
+ super(command, data)
+
+ @message = message
+ end
+end
+
+class Error < Response
+ Codes = {
+ NOT_LIST: 1,
+ ARG: 2,
+ PASSWORD: 3,
+ PERMISSION: 4,
+ UNKNOWN: 5,
+
+ NO_EXIST: 50,
+ PLAYLIST_MAX: 51,
+ SYSTEM: 52,
+ PLAYLIST_LOAD: 53,
+ UPDATE_ALREADY: 54,
+ PLAYER_SYNC: 55,
+ EXIST: 56
+ }
+
+ def self.parse (text)
+ text.match(/^\[(\d+)@(\d+)\] {([^}]*)} (.*?)$/).to_a[1 .. -1]
+ end
+
+ attr_reader :offset, :message
+
+ def initialize (command, data, code, offset, command_name, message)
+ if !command_name.empty? && command.name != command_name.to_sym
+ raise ArgumentError, 'the passed command object and the response command name do not match'
+ end
+
+ super(command, data)
+
+ @code = (code.is_a?(Integer) || Integer(code) rescue false) ? Codes.key(code.to_i) : code.to_sym.upcase
+ @offset = offset.to_i
+ @message = message
+
+ unless Codes[to_sym]
+ raise ArgumentError, 'the Error code does not exist'
+ end
+ end
+
+ def to_sym
+ @code
+ end
+
+ def to_i
+ Codes[to_sym]
+ end
+end
+
+end; end
15 lib/mpd/version.rb
@@ -0,0 +1,15 @@
+#--
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#++
+
+module MPD
+ def self.version
+ '0.16.8'
+ end
+end
16 mpd.gemspec
@@ -0,0 +1,16 @@
+Kernel.load 'lib/mpd/version.rb'
+
+Gem::Specification.new {|s|
+ s.name = 'mpd'
+ s.version = MPD.version
+ s.author = 'meh.'
+ s.email = 'meh@paranoici.org'
+ s.homepage = 'http://github.com/meh/ruby-mpd'
+ s.platform = Gem::Platform::RUBY
+ s.summary = 'MPD controller library.'
+
+ s.files = `git ls-files`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.require_paths = ['lib']
+}

0 comments on commit 6a271d8

Please sign in to comment.
Something went wrong with that request. Please try again.