3 # Copyright © 2012,2019-2021 Nick Bowler
5 # Send commands to MPD. Each command-line argument is quoted as necessary
6 # so it appears as a single argument at the protocol level. The result is
7 # printed to standard output.
9 # License GPLv3+: GNU General Public License version 3 or any later version.
10 # This is free software: you are free to change and redistribute it.
11 # There is NO WARRANTY, to the extent permitted by law.
17 use Encode qw(decode encode);
18 use Encode::Locale qw(decode_argv);
19 decode_argv(Encode::FB_CROAK);
21 binmode(STDOUT, ":utf8");
22 binmode(STDIN, ":utf8");
24 use Getopt::Long qw(:config gnu_getopt);
27 use lib "$FindBin::Bin";
30 my ($quiet, $verbose, $binary, $ignore_errors, $download);
35 Copyright © 2021 Nick Bowler
36 License GPLv3+: GNU General Public License version 3 or any later version.
37 This is free software: you are free to change and redistribute it.
38 There is NO WARRANTY, to the extent permitted by law.
43 my ($fh) = (@_, *STDERR);
45 print $fh "Usage: $0 [options] [command ...]\n";
46 print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
52 This is "mpdexec": a tool to send simple commands to MPD.
55 -h, --host=HOST Connect to the MPD server on HOST, overriding defaults.
56 -p, --port=PORT Connect to the MPD server on PORT, overriding defaults.
57 -q, --quiet Do not output any response messages. Only errors (on
58 standard error) or binary data (if enabled) are output.
59 -v, --verbose Print commands as they are submitted. If both --quiet
60 and --verbose are specified, --verbose has no effect.
62 Output raw binary response data, which is normally not
63 written. If FILE is specified, the data is written there.
64 Otherwise, --quiet is automatically enabled and the data
65 goes to standard output.
66 --download Enable automatic sequencing of albumart and readpicture
67 commands; if this option is specified, such commands
68 without offsets will be expanded into multiple commands
69 in order to download the entire file.
70 --ignore-errors In batch mode, continue submitting commands after errors.
71 -V, --version Print a version message and then exit.
72 -H, --help Print this message and then exit.
74 Report bugs to <nbowler\@draconx.ca>.
79 'host|h=s' => \$MPD::host,
80 'port|p=s' => \$MPD::port,
83 'no-quiet' => sub { $quiet = 0; },
84 'verbose|v' => \$verbose,
85 'no-verbose' => sub { $verbose = 0 },
86 'binary|b:s' => \$binary,
87 'no-binary' => sub { $binary = undef; },
89 'download' => \$download,
90 'no-download' => sub { $download = 0; },
92 'ignore-errors' => \$ignore_errors,
93 'no-ignore-errors' => sub { $ignore_errors = 0; },
95 'V|version' => sub { print_version(); exit },
96 'H|help' => sub { print_help(); exit },
97 ) or do { print_usage; exit 1 };
99 my $binfile = *STDOUT;
101 if ($binary ne "-") {
102 open(my $fh, ">", $binary) or die "failed to open $binary: $!";
106 $quiet = 1 if (defined($binary) && $binary eq "");
107 $verbose = 0 if ($quiet);
109 my $sock = MPD::connect(binmode => ":raw");
117 return 0 unless ($count);
118 my $rc = $sock->read($buf, $count) or die "$!";
119 if (defined($binary)) {
120 $binfile->write($buf) or die "$!";
127 my $cmd = encode('UTF-8', join(' ', @_), Encode::FB_QUIET);
128 print "$cmd\n" if ($verbose);
129 print $sock $cmd, $/;
132 my %downloadcmds = map { $_ => 1 } ( "albumart", "readpicture" );
136 # special case for "albumart" and "readpicture"; if no offset is
137 # specified (invalid command) we synthesize a sequence of albumart
138 # commands to retrieve the entire file.
139 if ($download && $downloadcmds{$_[0]} && @_ == 2) {
146 $_ = decode('UTF_8', $_, Encode::FB_QUIET);
149 last unless ($downloadseq);
150 print $sock encode('UTF-8',
152 Encode::FB_QUIET), $/;
156 if (/^binary: ([0-9]+)$/) {
157 print unless ($quiet);
161 $downloadseq = 0 unless ($1);
162 $_[$downloadseq] += $1;
167 last if ($ignore_errors);
170 print unless ($quiet);
176 mpd_exec(map { MPD::escape } @ARGV)
177 } elsif ($ignore_errors) {
178 while (<>) { chomp; mpd_exec($_); }
180 mpd_send("command_list_begin");
181 while (<>) { chomp; mpd_send($_); }
182 mpd_exec("command_list_end");
185 print $sock "close\n";