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