X-Git-Url: https://git.draconx.ca/gitweb/mpdhacks.git/blobdiff_plain/6b2e89cb66902820a6b5e31991bad3ae4c70296b..9913010f15f1fb7a3f331e655592fa6fc2807a12:/mpdmenu.pl diff --git a/mpdmenu.pl b/mpdmenu.pl index 8e597e7..3cc0e35 100755 --- a/mpdmenu.pl +++ b/mpdmenu.pl @@ -1,6 +1,6 @@ #!/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. @@ -18,12 +18,14 @@ use Encode::Locale qw(decode_argv); 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, @@ -33,48 +35,19 @@ use constant { 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 %artistids; -my $menu; +my ($topmenu, $menu); my $mode = "top"; - -# 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"; -} +my %artistids; 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 @@ -112,7 +85,7 @@ sub fvwm_label_escape { sub make_submenu { my $name = shift; $name =~ s/-/_/g; - unshift @_, ("exec", $SELF, "--menu=$name"); + unshift @_, ("exec", $SELF, "--topmenu=$topmenu", "--menu=$name"); fvwm_cmd("DestroyFunc", "Make$name"); fvwm_cmd("AddToFunc", "Make$name"); @@ -120,14 +93,14 @@ sub make_submenu { fvwm_cmd("DestroyMenu", $name); fvwm_cmd("AddToMenu", $name, "DynamicPopupAction", "Make$name"); - fvwm_cmd("AddToFunc", "KillMenuMPD", "I", "DestroyMenu", $name); + fvwm_cmd("AddToFunc", "Kill$topmenu", "I", "DestroyMenu", $name); fvwm_cmd("DestroyFunc", "Make$name"); fvwm_cmd("AddToFunc", "Make$name"); fvwm_cmd("+", "I", "DestroyMenu", $name); fvwm_cmd("+", "I", "-PipeRead", join(' ', map { fvwm_shell_literal } @_)); - fvwm_cmd("AddToFunc", "KillMenuMPD", "I", "DestroyFunc", "Make$name"); + fvwm_cmd("AddToFunc", "Kill$topmenu", "I", "DestroyFunc", "Make$name"); return ("Popup", $name); } @@ -243,8 +216,8 @@ sub top_track_cover { 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); } } @@ -300,7 +273,7 @@ sub get_tracks_by_work_mbid { 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/); @@ -329,13 +302,13 @@ sub get_tracks_by_work_mbid { # 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/); @@ -367,7 +340,7 @@ sub get_tracks_by_release_mbid { 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/); @@ -399,7 +372,7 @@ sub get_releases_by_artist_mbid { 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/); @@ -425,7 +398,7 @@ sub get_ids_by_filename { my ($file) = @_; my @results = (); - mpd_exec("playlistfind", "file", $file); + MPD::exec("playlistfind", "file", $file); while (<$sock>) { last if (/^OK/); die($_) if (/^ACK/); @@ -480,10 +453,10 @@ EOF } 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 { @@ -504,8 +477,8 @@ EOF } 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"; }, @@ -514,24 +487,20 @@ GetOptions( 'V|version' => sub { print_version(); exit }, 'H|help' => sub { print_help(); exit }, + + 'topmenu=s' => \$topmenu, # top menu name (for submenu generation) ) or do { print_usage; exit 1 }; +unless (defined $menu) { + $topmenu //= "MenuMPD"; + $menu = $topmenu . ($mode ne "top" ? $mode : ""); +} +$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; @@ -539,7 +508,7 @@ if ($mode eq "top") { $menu //= "MenuMPD"; - mpd_exec("status"); + MPD::exec("status"); while (<$sock>) { last if (/^OK/); die($_) if (/^ACK/); @@ -549,7 +518,7 @@ if ($mode eq "top") { } } - mpd_exec("currentsong"); + MPD::exec("currentsong"); while (<$sock>) { last if (/^OK/); die($_) if (/^ACK/); @@ -658,7 +627,8 @@ if ($mode eq "top") { my $entry = $matches->{$mbid}; my $thumb = shift @thumbs; - my @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid"); + my @submenu = make_submenu("$topmenu-$mbid", + "--album-id=$mbid"); fvwm_cmd("AddToMenu", $menu, $thumb . fvwm_label_escape($entry->{Album}), @submenu);