#!/usr/bin/perl
#
-# Copyright © 2008,2010,2012,2019 Nick Bowler
+# Copyright © 2008,2010,2012,2020 Nick Bowler
#
# Silly little script to generate an FVWM menu with various bits of MPD
# status information and controls.
decode_argv(Encode::FB_CROAK);
binmode(STDOUT, ":utf8");
-use IO::Socket::INET6;
use Getopt::Long qw(:config gnu_getopt);
use Scalar::Util qw(reftype);
use List::Util qw(any max);
use FindBin;
+use lib "$FindBin::Bin";
+use MPDHacks;
+
use constant {
MPD_MJR_MIN => 0,
MPD_MNR_MIN => 21,
my $SELF = "$FindBin::Bin/$FindBin::Script";
my $MUSIC = $ENV{MUSIC} // "/srv/music";
-my $host = $ENV{MPD_HOST} // "localhost";
-my $port = $ENV{MPD_PORT} // "6600";
my $sock;
my ($albumid, $trackid);
my $mode = "top";
my %artistids;
-# Quotes the argument so that it is presented as a single argument to MPD
-# at the protocol level. This also works OK for most FVWM arguments.
-sub escape {
- my $s = @_[0] // $_;
-
- # No way to encode literal newlines in the protocol, so we
- # convert any newlines in the arguments into a space, which
- # can help with quoting.
- $s =~ s/\n/ /g;
-
- if (/[ \t\\"]/) {
- $s =~ s/[\\"]/\\$&/g;
- return "\"$s\"";
- }
-
- $s =~ s/^\s*$/"$&"/;
- return $s;
-}
-
-# Submit a command to the MPD server; each argument to this function
-# is quoted and sent as a single argument to MPD.
-sub mpd_exec {
- my $cmd = join(' ', map { escape } @_);
-
- print $sock "$cmd\n";
-}
-
sub fvwm_cmd_unquoted {
print join(' ', @_), "\n";
}
sub fvwm_cmd {
- fvwm_cmd_unquoted(map { escape } @_);
+ fvwm_cmd_unquoted(map { MPD::escape } @_);
}
# Quotes the argument in such a way that it is passed unadulterated by
my $cover = mpd_cover_filename($file);
$cover = fvwm_shell_literal($cover // $file);
- fvwm_cmd_unquoted("AddToMenu", escape($menu),
- escape($entry->{thumb}),
+ fvwm_cmd_unquoted("AddToMenu", MPD::escape($menu),
+ MPD::escape($entry->{thumb}),
"Exec", "exec", "geeqie", $cover);
}
}
my $entry;
foreach my $mbid (@_) {
- mpd_exec("search", "(MUSICBRAINZ_WORKID == \"$mbid\")");
+ MPD::exec("search", "(MUSICBRAINZ_WORKID == \"$mbid\")");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
# Currently tracks are considered "related" if their associated recordings
# have at least one work in common.
sub get_tracks_by_track_mbid {
- my ($mbid) = @_;
+ my ($mbid, $tagname) = (@_, "MUSICBRAINZ_RELEASETRACKID");
my %source;
my %matches;
my $entry;
return \%matches unless ($mbid);
- mpd_exec("search", "(MUSICBRAINZ_RELEASETRACKID == \"$mbid\")");
+ MPD::exec("search", "(MUSICBRAINZ_RELEASETRACKID == \"$mbid\")");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
my $entry;
return \%matches unless ($mbid);
- mpd_exec("search", "(MUSICBRAINZ_ALBUMID == \"$mbid\")");
+ MPD::exec("search", "(MUSICBRAINZ_ALBUMID == \"$mbid\")");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
my $entry;
foreach my $mbid (@_) {
- mpd_exec("search", "(MUSICBRAINZ_ARTISTID == \"$mbid\")");
+ MPD::exec("search", "(MUSICBRAINZ_ARTISTID == \"$mbid\")");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
my ($file) = @_;
my @results = ();
- mpd_exec("playlistfind", "file", $file);
+ MPD::exec("playlistfind", "file", $file);
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
}
sub print_usage {
- my $fh = $_[1] // *STDERR;
+ my ($fh) = (@_, *STDERR);
print $fh "Usage: $0 [options]\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 {
}
GetOptions(
- 'host|h=s' => \$host,
- 'port|p=s' => \$port,
+ 'host|h=s' => \$MPD::host,
+ 'port|p=s' => \$MPD::port,
'menu|m=s' => \$menu,
'artist-id=s' => sub { $artistids{$_[1]} = 1; $mode = "artist"; },
$topmenu //= $menu;
# Connect to MPD.
-$sock = new IO::Socket::INET6(
- PeerAddr => $host,
- PeerPort => $port,
- Proto => 'tcp',
- Timeout => 2
-) or die("could not open socket: $!.\n");
-binmode($sock, ":utf8");
-
-die("could not connect to MPD: $!.\n")
- if (!(<$sock> =~ /^OK MPD ([0-9]+)\.([0-9]+)\.([0-9]+)$/));
-
-die("MPD version $1.$2.$3 insufficient.\n")
- if ( ($1 < MPD_MJR_MIN)
- || ($1 == MPD_MJR_MIN && $2 < MPD_MNR_MIN)
- || ($1 == MPD_MJR_MIN && $2 == MPD_MNR_MIN && $3 < MPD_REV_MIN));
+$sock = MPD::connect();
+die("MPD version $MPD::major.$MPD::minor.$MPD::revision insufficient.")
+ unless MPD::min_version(MPD_MJR_MIN, MPD_MNR_MIN, MPD_REV_MIN);
if ($mode eq "top") {
my %current;
$menu //= "MenuMPD";
- mpd_exec("status");
+ MPD::exec("status");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);
}
}
- mpd_exec("currentsong");
+ MPD::exec("currentsong");
while (<$sock>) {
last if (/^OK/);
die($_) if (/^ACK/);