Jan 2, 2017 Tags: muzak, programming, ruby
Preword: This is the fifth weekly post on muzak’s development. You can find the rest under the muzak tag. It might also be the last (regular) post in the series, since muzak is increasingly stable and most of my “development” has shifted towards custom plugins and commands for my own convenience.
I did a little bit more work than I expected to this week, so here’s an update post.
Quick statistics, as usual:
git diff --stat HEAD HEAD~31
)muzakd
communication via socketsmuzakd
now uses a TCP socket instead of a FIFO, which presents two major
advantages:
Muzak commands can now respond to the client itself, rather than print out or log information in the daemon process.
Muzak can now, in theory, be run remotely. Since I’ve only implemented support for one (local) player, this hasn’t actually been tested.
By default muzakd
runs on localhost
port 2669
, but this can be changed
by setting daemon-port
and/or daemon-host
in ~/.config/muzak/muzak.yml
.
Piggybacking on the new TCP socket above is command responses, which provide a standard response structure for muzak commands.
Command responses are always ruby hashes, which muzakd
handily serializes
into JSON for clients to consume. For example, here’s muzak-cmd
invocation:
1
$ muzak-cmd now-playing > response.json
response.json
then contains something like this (pretty-printed):
1
2
3
4
5
6
7
8
9
{
"response" : {
"data" : {
"playing" : "HiiiPower by Kendrick Lamar on Section.80"
},
"method" : "now_playing",
"error" : null
}
}
These responses are easily constructed using Utils#build_response
:
1
2
3
4
5
6
7
8
9
module Muzak
module Cmd
def do_something
build_response data: {
message: "I lied, this command doesn't really do anything"
}
end
end
end
Complementing the new response system is full user documentation of commands (including descriptions, syntax, and examples) via COMMANDS.md.
This file gets included in YARD, yielding a pretty page in the online documentation.
Previously, muzak “handled” command arguments by passing them as a variable
argument list (*args
) and letting each individual command figure them out
for themselves. This resulted in a bunch of unnecessary boilerplate
and ugly guard methods like warn_arity
and fail_arity
.
This has been replaced with arity checking in Muzak::Instance#command
.
When the number of arguments provided doesn’t match the command’s arity,
a helpful error response is returned:
1
2
3
4
5
6
$ muzak-cmd playlist-add-artist | json_pp
{
"response" : {
"error" : "wrong number of arguments (given 0, expected 1+)"
}
}
Similarly, Muzak::Instance#method_missing
has been removed in
favor of a check against Muzak::Cmd.commands
.
There are lots of small improvements to muzak’s performance in this week’s changes. In particular:
Muzak::Player::MPV
now memoizes the currently playing song, saving us
from potentially expensive I/O and tag parsing during song_loaded
dispatch.
jukebox
now enqueues the jukeboxed songs in a separate thread to avoid
blocking the main instance.
muzakd
spawns a new thread for each client connection, further improving
muzak’s responsiveness (and potentially further expanding the race condition
surface).
bin/muzak
I basically haven’t used bin/muzak
since the first week of muzak’s
development. As such, I mostly ignored it while making UI/UX decisions. This
has culminated in it being mostly useless now that all commands return response
hashes (instead of printing useful output, as they originally did).
I’ve removed bin/muzak
from the Gem specification, and I’ll probably remove
it from the repository entirely shortly.
As always, thanks for reading!
- William
P.S.: I haven’t been keeping track of development statistics or pace in the other muzak repositories (namely muzak-utils, muzak-commands, and muzak-plugins). They’re all worth taking a look at if you want to augment your muzak flow.