]> git.draconx.ca Git - mpdhacks.git/blobdiff - mpdexec.pl
mpdthumb: Fix failure when readpicture/albumart both return data.
[mpdhacks.git] / mpdexec.pl
index 0d477dae465d0923b297fea31d2af3919508d54a..e83146bcdc065489b6f6e5e4777db430cb19743a 100755 (executable)
@@ -1,6 +1,6 @@
 #!/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
@@ -20,18 +20,19 @@ decode_argv(Encode::FB_CROAK);
 
 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.
@@ -39,10 +40,10 @@ EOF
 }
 
 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 {
@@ -55,15 +56,17 @@ Options:
   -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.
@@ -73,11 +76,13 @@ EOF
 }
 
 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; },
 
@@ -99,33 +104,9 @@ if ($binary) {
        }
 }
 $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) = @_;
@@ -142,18 +123,25 @@ sub read_binary {
        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);
 
@@ -185,12 +173,13 @@ sub mpd_exec {
 }
 
 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";