]> git.draconx.ca Git - mpdhacks.git/blob - mpdexec.pl
mpdthumb: Add --help and --version options.
[mpdhacks.git] / mpdexec.pl
1 #!/usr/bin/env perl
2 #
3 # Copyright © 2012,2019-2020 Nick Bowler
4 #
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.
8 #
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.
12
13 use strict;
14
15 use utf8;
16
17 use Encode qw(decode encode);
18 use Encode::Locale qw(decode_argv);
19 decode_argv(Encode::FB_CROAK);
20
21 binmode(STDOUT, ":utf8");
22 binmode(STDIN, ":utf8");
23
24 use Getopt::Long qw(:config gnu_getopt);
25
26 use FindBin;
27 use lib "$FindBin::Bin";
28 use MPDHacks;
29
30 my ($quiet, $binary, $ignore_errors, $download);
31
32 sub print_version {
33         print <<EOF
34 mpdexec.pl 0.8
35 Copyright © 2019 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.
39 EOF
40 }
41
42 sub print_usage {
43         my ($fh) = (@_, *STDERR);
44
45         print $fh "Usage: $0 [options] [command ...]\n";
46         print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
47 }
48
49 sub print_help {
50         print_usage(*STDOUT);
51         print <<EOF
52 This is "mpdexec": a tool to send simple commands to MPD.
53
54 Options:
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   -b, --binary[=FILE]
60                     Output raw binary response data, which is normally not
61                     written.  If FILE is specified, the data is written there.
62                     Otherwise, --quiet is automatically enabled and the data
63                     goes to standard output.
64   --download        Enable automatic sequencing of albumart commands; if this
65                     option is specified, albumart commands without offsets will
66                     be expanded into multiple commands in order to download the
67                     entire file.
68   --ignore-errors   In batch mode, continue submitting commands after errors.
69   -V, --version     Print a version message and then exit.
70   -H, --help        Print this message and then exit.
71
72 Report bugs to <nbowler\@draconx.ca>.
73 EOF
74 }
75
76 GetOptions(
77         'host|h=s'         => \$MPD::host,
78         'port|p=s'         => \$MPD::port,
79
80         'quiet|q'          => \$quiet,
81         'no-quiet'         => sub { $quiet = 0; },
82         'binary|b:s'       => \$binary,
83         'no-binary'        => sub { $binary = undef; },
84
85         'download'         => \$download,
86         'no-download'      => sub { $download = 0; },
87
88         'ignore-errors'    => \$ignore_errors,
89         'no-ignore-errors' => sub { $ignore_errors = 0; },
90
91         'V|version'        => sub { print_version(); exit },
92         'H|help'           => sub { print_help(); exit },
93 ) or do { print_usage; exit 1 };
94
95 my $binfile = *STDOUT;
96 if ($binary) {
97         if ($binary ne "-") {
98                 open(my $fh, ">", $binary) or die "failed to open $binary: $!";
99                 $binfile = $fh;
100         }
101 }
102 $quiet = 1 if (defined($binary) && $binary eq "");
103
104 my $sock = MPD::connect(binmode => ":raw");
105
106 sub read_binary {
107         my ($count) = @_;
108         my $buf;
109
110         binmode($binfile);
111
112         return 0 unless ($count);
113         my $rc = $sock->read($buf, $count) or die "$!";
114         if (defined($binary)) {
115                 $binfile->write($buf) or die "$!";
116         }
117
118         return $rc;
119 }
120
121 sub mpd_exec {
122         my $downloadseq;
123
124         # special case for "albumart"; if no offset is specified
125         # (invalid command) we synthesize a sequence of albumart
126         # commands to retrieve the entire file.
127         if ($download && $_[0] eq "albumart" && @_ == 2) {
128                 $_[2] = 0;
129                 $downloadseq = 2;
130         }
131
132         print $sock encode('UTF-8', join(' ', @_), Encode::FB_QUIET), $/;
133         while (<$sock>) {
134                 $_ = decode('UTF_8', $_, Encode::FB_QUIET);
135
136                 if (/^OK/) {
137                         last unless ($downloadseq);
138                         print $sock encode('UTF-8',
139                                            join(' ', @_),
140                                            Encode::FB_QUIET), $/;
141                         next;
142                 }
143
144                 if (/^binary: ([0-9]+)$/) {
145                         print unless ($quiet);
146                         read_binary($1);
147
148                         if ($downloadseq) {
149                                 $downloadseq = 0 unless ($1);
150                                 $_[$downloadseq] += $1;
151                         }
152                 } elsif (/^ACK/) {
153                         *STDOUT->flush;
154                         print STDERR;
155                         last if ($ignore_errors);
156                         exit 1;
157                 } else {
158                         print unless ($quiet);
159                 }
160         }
161 }
162
163 if (@ARGV) {
164         mpd_exec(map { MPD::escape($_) } @ARGV)
165 } else {
166         while (<>) {
167                 chomp;
168                 mpd_exec($_);
169         }
170 }
171
172 print $sock "close\n";
173 close $sock;