#!/usr/bin/env perl
#
-# Copyright © 2012,2019 Nick Bowler
+# Copyright © 2012,2019-2021 Nick Bowler
#
# Send commands to MPD. Each command-line argument is quoted as necessary
# so it appears as a single argument at the protocol level. The result is
binmode(STDOUT, ":utf8");
binmode(STDIN, ":utf8");
-use IO::Socket::INET6;
use Getopt::Long qw(:config gnu_getopt);
-my $host = $ENV{MPD_HOST} // "localhost";
-my $port = $ENV{MPD_PORT} // 6600;
-my ($quiet, $binary, $ignore_errors, $download);
+use FindBin;
+use lib "$FindBin::Bin";
+use MPDHacks;
+
+my ($quiet, $verbose, $binary, $ignore_errors, $download);
sub print_version {
print <<EOF
-mpdexec.pl 0.8
-Copyright © 2019 Nick Bowler
+mpdexec.pl 0.9
+Copyright © 2021 Nick Bowler
License GPLv3+: GNU General Public License version 3 or any later version.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
}
sub print_usage {
- my $fh = $_[1] // *STDERR;
+ my ($fh) = (@_, *STDERR);
print $fh "Usage: $0 [options] [command ...]\n";
- print "Try $0 --help for more information.\n" unless (@_ > 0);
+ print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
}
sub print_help {
-p, --port=PORT Connect to the MPD server on PORT, overriding defaults.
-q, --quiet Do not output any response messages. Only errors (on
standard error) or binary data (if enabled) are output.
+ -v, --verbose Print commands as they are submitted. If both --quiet
+ and --verbose are specified, --verbose has no effect.
-b, --binary[=FILE]
Output raw binary response data, which is normally not
written. If FILE is specified, the data is written there.
Otherwise, --quiet is automatically enabled and the data
goes to standard output.
- --download Enable automatic sequencing of albumart commands; if this
- option is specified, albumart commands without offsets will
- be expanded into multiple commands in order to download the
- entire file.
+ --download Enable automatic sequencing of albumart and readpicture
+ commands; if this option is specified, such commands
+ without offsets will be expanded into multiple commands
+ in order to download the entire file.
--ignore-errors In batch mode, continue submitting commands after errors.
-V, --version Print a version message and then exit.
-H, --help Print this message and then exit.
}
GetOptions(
- 'host|h=s' => \$host,
- 'port|p=s' => \$port,
+ 'host|h=s' => \$MPD::host,
+ 'port|p=s' => \$MPD::port,
'quiet|q' => \$quiet,
'no-quiet' => sub { $quiet = 0; },
+ 'verbose|v' => \$verbose,
+ 'no-verbose' => sub { $verbose = 0 },
'binary|b:s' => \$binary,
'no-binary' => sub { $binary = undef; },
}
}
$quiet = 1 if (defined($binary) && $binary eq "");
+$verbose = 0 if ($quiet);
-my $sock = new IO::Socket::INET6(
- PeerAddr => $host,
- PeerPort => $port,
- Proto => 'tcp',
-) or die "failed to connect to MPD: $!";
-#binmode($sock, ":utf8");
-binmode($sock);
-
-if (!(<$sock> =~ /^OK MPD ([0-9]+)\.([0-9]+)\.([0-9]+)$/)) {
- die "MPD failed to announce version: $!";
-}
-
-sub mpd_escape {
- ($_) = @_;
-
- # No way to encode literal newlines in the protocol, so we convert
- # any newlines in the arguments into a space, which can help with
- # shell quoting.
- s/\n/ /g;
-
- if (/[ \t\\"]/) {
- s/[\\"]/\\$&/g;
- return "\"$_\"";
- }
- return $_;
-}
+my $sock = MPD::connect(binmode => ":raw");
sub read_binary {
my ($count) = @_;
return $rc;
}
+sub mpd_send {
+ my $cmd = encode('UTF-8', join(' ', @_), Encode::FB_QUIET);
+ print "$cmd\n" if ($verbose);
+ print $sock $cmd, $/;
+}
+
+my %downloadcmds = map { $_ => 1 } ( "albumart", "readpicture" );
sub mpd_exec {
my $downloadseq;
- # special case for "albumart"; if no offset is specified
- # (invalid command) we synthesize a sequence of albumart
+ # special case for "albumart" and "readpicture"; if no offset is
+ # specified (invalid command) we synthesize a sequence of albumart
# commands to retrieve the entire file.
- if ($download && $_[0] eq "albumart" && @_ == 2) {
+ if ($download && $downloadcmds{$_[0]} && @_ == 2) {
$_[2] = 0;
$downloadseq = 2;
}
- print $sock encode('UTF-8', join(' ', @_), Encode::FB_QUIET), $/;
+ mpd_send(@_);
while (<$sock>) {
$_ = decode('UTF_8', $_, Encode::FB_QUIET);
}
if (@ARGV) {
- mpd_exec(map { mpd_escape($_) } @ARGV)
+ mpd_exec(map { MPD::escape } @ARGV)
+} elsif ($ignore_errors) {
+ while (<>) { chomp; mpd_exec($_); }
} else {
- while (<>) {
- chomp;
- mpd_exec($_);
- }
+ mpd_send("command_list_begin");
+ while (<>) { chomp; mpd_send($_); }
+ mpd_exec("command_list_end");
}
print $sock "close\n";