lib/radiodan/adapter/mpd.rb in radiodan-0.0.4 vs lib/radiodan/adapter/mpd.rb in radiodan-1.0.0
- old
+ new
@@ -3,26 +3,30 @@
require_relative './mpd/connection'
require_relative './mpd/playlist_parser'
class Radiodan
class MPD
+ class AckError < Exception; end
include Logging
extend Forwardable
def_delegators :@connection, :cmd
- COMMANDS = %w{stop pause clear play next previous}
+ COMMANDS = %w{stop pause clear play next previous enqueue search update}
+ SEARCH_SCOPE = %w{artist album title track name genre date composer performer comment disc filename any}
attr_reader :player
def initialize(options={})
@connection = Connection.new(options)
+ @connection.cmd('clear')
+ @connection.cmd('update')
end
-
+
def player=(player)
@player = player
- # register typical player commands
+ # register available player commands
COMMANDS.each do |command|
@player.register_event command do |data|
if data
self.send(command, data)
else
@@ -33,53 +37,134 @@
# register new playlist events
@player.register_event :playlist do |playlist|
self.playlist = playlist
end
+
+ # register volume changes
+ @player.register_event :volume do |volume|
+ self.volume = volume
+ end
end
def playlist=(playlist)
# get rid of current playlist, stop playback
clear
+
+ # set random & repeat
+ cmd(%Q{random #{boolean_to_s(playlist.random?)}})
+ cmd(%Q{repeat #{boolean_to_s(playlist.repeat?)}})
+
+ # set volume
+ begin
+ volume = playlist.volume
+ rescue AckError => e
+ logger.error e.msg
+ end
- if enqueue playlist
- play playlist.position
+ if playlist.empty?
+ logger.error 'Playlist empty, nothing to do'
+ return false
+ end
+
+ if enqueue playlist.tracks
+ # set for seek position (will play from seek point)
+ cmd(%Q{seek #{playlist.position} #{Integer(playlist.seek)}})
else
raise "Cannot load playlist #{playlist}"
end
end
+
+ def volume=(new_volume)
+ cmd(%Q{setvol #{Integer(new_volume)}})
+ end
- def enqueue(playlist)
- playlist.tracks.each do |track|
+ def enqueue(tracks)
+ tracks.each do |track|
cmd(%Q{add "#{track[:file]}"})
end
end
def play(song_number=nil)
cmd("play #{song_number}")
end
def playlist
- status = cmd("status")
- tracks = cmd("playlistinfo")
+ begin
+ status = cmd('status')
+ tracks = cmd('playlistinfo')
- PlaylistParser.parse(status, tracks)
+ playlist = PlaylistParser.parse(status, tracks)
+ playlist
+ rescue Playlist::StateError,
+ Playlist::ModeError,
+ Playlist::PositionError,
+ Playlist::SeekError,
+ Playlist::VolumeError => e
+ logger.warn("Playlist parsing raised error: #{e}")
+ retry
+ end
end
+
+ # search :artist => "Bob Marley", :exact => true
+ # search :filename => './bob.mp3'
+ # search "Bob Marley"
+ def search(args)
+ if args.nil?
+ logger.error 'no query found'
+ return []
+ end
+
+ if args.to_s == args
+ args = {'any' => args}
+ end
+
+ if args.delete(:exact)
+ command = 'find'
+ else
+ command = 'search'
+ end
+
+ if args.keys.size > 1
+ raise 'Too many arguments for search'
+ end
+
+ scope = args.keys.first.to_s
+ term = args.values.first
+
+ unless SEARCH_SCOPE.include?(scope)
+ raise "Unknown search scope #{scope}"
+ end
+
+ cmd_string = %Q{#{command} #{scope} "#{term}"}
+
+ tracks = cmd(cmd_string)
+ if tracks.respond_to?(:collect)
+ tracks.collect { |t| Track.new(t) }
+ else
+ []
+ end
+ end
+
def respond_to?(method)
if COMMANDS.include?(method.to_s)
true
else
super
end
end
- private
def method_missing(method, *args, &block)
if COMMANDS.include?(method.to_s)
cmd(method.to_s, *args, &block)
else
super
end
+ end
+
+ private
+ def boolean_to_s(bool)
+ bool == true ? '1' : '0'
end
end
end