MPD::Client

Crystal CI GitHub release Docs License

Concurrent Music Player Daemon client written entirely in Crystal

Installation

Add this to your application's shard.yml:

dependencies:
  crystal_mpd:
    github: mamantoha/crystal_mpd

Usage

require "crystal_mpd"

All functionality is contained in the MPD::Client class. Creating an instance of this class is as simple as:

client = MPD::Client.new("localhost", 6600)

You can also omit the host and port, and it will use the defaults:

client = MPD::Client.new("localhost")
client = MPD::Client.new

You can connect to a local socket (UNIX domain socket), specify an absolute path:

client = MPD::Client.new("/run/mpd/socket")

If a password specified for access to MPD:

client = MPD::Client.new("localhost", password: "password")

The client library can be used as follows:

puts client.version # print the mpd version
client.play(2)      # begins playing the playlist at song number 2
puts client.status  # print the current status of the player and the volume level
client.close        # send the close command
client.disconect    # disconnect from the server

Check MPD::Client source for supported commands.

To use all crystal_mpd functions you should use the latest stable MPD version (0.23.x).

Command lists

Command lists documentation.

To facilitate faster adding of files etc. you can pass a list of commands all at once using a command list. The command list begins with command_list_ok_begin and ends with command_list_end.

It does not execute any commands until the list has ended. The return value is whatever the return for a list of commands is. On success for all commands, OK is returned.

If a command fails, no more commands are executed and the appropriate ACK error is returned.

If command_list_ok_begin is used, list_OK is returned for each successful command executed in the command list.

client.command_list_ok_begin # start a command list
client.update                # insert the update command into the list
client.status                # insert the status command into the list
client.command_list_end      # result will be a Array with the results

or

client.with_command_list do
  client.update
  client.status
end

Ranges

Ranges documentation.

Some commands(e.g. move, delete, load, shuffle, playlistinfo) allow integer ranges(START:END) instead of numbers, specifying a range of songs. This is done by using MPD::Range. crystal_mpd correctly handles inclusive and exclusive ranges (1..10 vs 1...10). Negative range end means that we want the range to span until the end of the list.

# move songs 1, 2 and 3 to position 10 (and 11 and 12)
client.move(1..3, 10)

# deleve songs 1, 2 and 3 from playlist
client.delete(0..2)

# deleve songs 1 and 2
client.delete(0...2)

With negative range end MPD will assumes the biggest possible number then:

# delete all songs from the current playlist, except for the firts ten
client.delete(10..-1)

End-less range end MPD will also assumes the biggest possible number then:

# delete all songs from the current playlist, except for the firts ten
client.delete(10..)
# or
client.delete(10...)

With begin-less range begin is equal to 0:

# delete first 1, 2 and 3 songs from the current playlist
client.delete(..2)

# delete first 1 and 2 songs from the current playlist
client.delete(...2)

Filters

Filters documentation

All commands which search for songs (find, search, searchadd, searchaddpl, findadd, list, and count) share a common filter syntax.

The find commands are case sensitive, which search and related commands ignore case.

client.search("(any =~ 'crystal')")
client.searchaddpl("alt_rock", "(genre == 'Alternative Rock')", sort: "-ArtistSort", window: (5..10))
client.list("filename", "((artist == 'Linkin Park') AND (date == '2003'))")

Callbacks

Callbacks are a simple way to make your client respond to events, rather that have to continuously ask the server for updates. This is done by having a background thread continuously check the server for changes.

To make use of callbacks, you need to:

  1. Create a MPD client instance with callbacks enabled.

    client = MPD::Client.new(with_callbacks: true)
  2. Setup a callback to be called when something happens.

    client.on :state do |state|
      puts "[#{Time.local}] State was change to #{state}"
    end

crystal_mpd supports callbacks for any of the keys returned by MPD::Client#status.

Here's the full list of events:

client = MPD::Client.new(with_callbacks: true)
client.callbacks_timeout = 2.seconds

client.on :state do |state|
  puts "[#{Time.local}] State was change to #{state}"
end

client.on :song do
  if (current_song = client.currentsong)
    puts "[#{Time.local}] 🎵 #{current_song["Artist"]} - #{current_song["Title"]}"
  end
end

loop { sleep 1 }

The above will connect to the server like normal, but this time it will create a new thread that loops until you issue an exit. This loop checks the server, then sleeps for 2 seconds, then loops.

Binary responses

Some commands can return binary data.

client = MPD::Client.new

if (current_song = client.currentsong)
  if (response = client.albumart(current_song["file"]))
    data, binary = response
    # data # => {"size" => "30219", "type" => "image/jpeg", "binary" => "5643"}

    extension = MIME.extensions(data["type"]).first? || ".png"

    file = File.open("cover#{extension}", "w")
    file.write(binary.to_slice)
  end
end

The above will locate album art for the current song and save image to cover.jpg file.

Logging

require "crystal_mpd"

client = MPD::Client.new

MPD::Log.level = :debug
MPD::Log.backend = ::Log::IOBackend.new

Development

Install dependencies:

shards

To run test:

crystal spec

Who's using MPD::Client

If you're using MPD::Client and would like to have your application added to this list, just submit a PR!

Contributing

  1. Fork it (https://github.com/mamantoha/crystal_mpd/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

License

Copyright: 2018-2024 Anton Maminov (anton.maminov@gmail.com)

This library is distributed under the MIT license. Please see the LICENSE file.