]> git.draconx.ca Git - mpdhacks.git/blobdiff - mpdmenu.pl
mpdmenu: Improve support for standalone recordings.
[mpdhacks.git] / mpdmenu.pl
index 3cc0e350d02e1d85f68377153163b41432de9145..2b5fcaddf3a18abcd5d0355277dc2b66666ba8d4 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 #
-# Copyright © 2008,2010,2012,2020 Nick Bowler
+# Copyright © 2008,2010,2012,2020-2021 Nick Bowler
 #
 # Silly little script to generate an FVWM menu with various bits of MPD
 # status information and controls.
@@ -37,7 +37,7 @@ my $SELF = "$FindBin::Bin/$FindBin::Script";
 my $MUSIC = $ENV{MUSIC}    // "/srv/music";
 my $sock;
 
-my ($albumid, $trackid);
+my ($albumid, $albumname, $trackid, $recordingid);
 my ($topmenu, $menu);
 my $mode = "top";
 my %artistids;
@@ -228,7 +228,13 @@ sub top_track_title {
        my @submenu;
 
        my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_RELEASETRACKID");
-       @submenu = make_submenu("$menu-$mbid", "--track-id=$mbid") if $mbid;
+       if ($mbid) {
+               @submenu = make_submenu("$menu-$mbid", "--track-id=$mbid")
+       } else {
+               ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID");
+               @submenu = make_submenu("$menu-track-$mbid", "--recording-id=$mbid")
+                       if ($mbid);
+       }
 
        fvwm_cmd("AddToMenu", $menu,
                 fvwm_label_escape("Title:\t$entry->{Title}"),
@@ -257,9 +263,19 @@ sub top_track_artist {
 sub top_track_album {
        my ($entry) = @_;
        my @submenu;
+       my $mbid;
+
+       if (($mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID")) {
+               @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid");
+       } elsif (($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID")) {
+               # Standalone recording
+               my @a = get_track_metadata($entry, "MUSICBRAINZ_ARTISTID");
+               my ($album) = get_track_metadata($entry, "Album");
 
-       my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID");
-       @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid") if $mbid;
+               @submenu = make_submenu("$menu-$mbid", "--album-name=$album",
+                                       map { "--artist-id=$_" } @a);
+
+       }
 
        fvwm_cmd("AddToMenu", $menu,
                 fvwm_label_escape("Album:\t$entry->{Album}"),
@@ -308,7 +324,7 @@ sub get_tracks_by_track_mbid {
        my $entry;
 
        return \%matches unless ($mbid);
-       MPD::exec("search", "(MUSICBRAINZ_RELEASETRACKID == \"$mbid\")");
+       MPD::exec("search", "($tagname == \"$mbid\")");
        while (<$sock>) {
                last if (/^OK/);
                die($_) if (/^ACK/);
@@ -332,6 +348,10 @@ sub get_tracks_by_track_mbid {
        return \%matches;
 }
 
+sub get_tracks_by_recording_mbid {
+       return get_tracks_by_track_mbid($_[0], "MUSICBRAINZ_TRACKID");
+}
+
 # Given a release MBID, return a hash reference containing all its
 # associated tracks in the MPD database.  The hash keys are filenames.
 sub get_tracks_by_release_mbid {
@@ -362,13 +382,28 @@ sub get_tracks_by_release_mbid {
        return \%matches;
 }
 
-# Given an artist MBID, return a hash reference containing associated
-# releases in the MPD database.  The hash keys are release MBIDs.
+# Insert the given entry into the referenced hash if it represents a
+# standalone recording (not associated with a release).  The recording
+# MBID is used as the hash key.
+sub check_standalone {
+       my ($outhash, $entry) = @_;
+       my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID");
+
+       return if exists $entry->{MUSICBRAINZ_ALBUMID};
+       $outhash->{$mbid} = $entry if ($mbid);
+}
+
+# Given an artist MBID, return a list of two hash refererences.  The
+# first contains the associated releases in the MPD database and the
+# hash keys are release MBIDs.  The second contains the artist's
+# standalone recordings and the hash keys are recording MBIDs.
+#
+# In scalar context only the release hash is returned.
 #
 # Since MPD returns results on a per-track basis, each entry in the
 # hash has the metadata for one unspecified track from that release.
 sub get_releases_by_artist_mbid {
-       my %releases;
+       my (%releases, %standalones);
        my $entry;
 
        foreach my $mbid (@_) {
@@ -379,6 +414,7 @@ sub get_releases_by_artist_mbid {
 
                        if (/^(\w+): (.*)$/) {
                                if ($1 eq "file") {
+                                       check_standalone(\%standalones, $entry);
                                        $entry = {};
                                } elsif ($1 eq "MUSICBRAINZ_ALBUMID") {
                                        $releases{$2} //= $entry;
@@ -387,9 +423,10 @@ sub get_releases_by_artist_mbid {
                                add_track_metadata($entry, $1, $2);
                        }
                }
+               check_standalone(\%standalones, $entry);
        }
 
-       return \%releases;
+       return wantarray ? (\%releases, values %standalones) : \%releases;
 }
 
 # Given a filename, return the IDs (if any) for that file in the
@@ -411,6 +448,24 @@ sub get_ids_by_filename {
        return @results;
 }
 
+sub update_entry_ids {
+       my @notqueued = ();
+
+       foreach my $entry (@_) {
+               unless (exists $entry->{Id}) {
+                       my ($id) = get_ids_by_filename($entry->{file});
+                       if (defined $id) {
+                               $entry->{Id} = $id;
+                       } else {
+                               push @notqueued, $entry;
+                               next;
+                       }
+               }
+       }
+
+       return @notqueued;
+}
+
 # albumsort(matches, a, b)
 #
 # Sort hash keys (a, b) by disc/track number for album menus.
@@ -444,8 +499,8 @@ sub menu_trackname {
 
 sub print_version {
        print <<EOF
-mpdmenu.pl 0.8
-Copyright © 2019 Nick Bowler
+mpdmenu.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.
@@ -469,28 +524,37 @@ Options:
   -p, --port=PORT   Connect to the MPD server on PORT, overriding defaults.
   -m, --menu=NAME   Set the name of the generated menu.
   --album-id=MBID   Generate a menu for the given release MBID.
+  --album-name=NAME
+                    Generate a menu for standalone tracks with the given
+                   "album" NAME.  An artist MBID must be supplied.
   --artist-id=MBID  Generate a menu for the given artist MBID.
   --track-id=MBID   Generate a menu for the given track MBID.
+  --recording-id=MBID
+                    Generate a menu for the given recording MBID.
   -V, --version     Print a version message and then exit.
   -H, --help        Print this message and then exit.
 EOF
 }
 
 GetOptions(
-       'host|h=s'    => \$MPD::host,
-       'port|p=s'    => \$MPD::port,
-       'menu|m=s'    => \$menu,
+       'host|h=s'       => \$MPD::host,
+       'port|p=s'       => \$MPD::port,
+       'menu|m=s'       => \$menu,
 
-       'artist-id=s' => sub { $artistids{$_[1]} = 1; $mode = "artist"; },
-       'album-id=s'  => sub { $albumid = $_[1]; $mode = "album"; },
-       'track-id=s'  => sub { $trackid = $_[1]; $mode = "track"; },
+       'artist-id=s'    => sub { $artistids{$_[1]} = 1; $mode = "artist"; },
+       'album-id=s'     => sub { $albumid = $_[1]; $mode = "album"; },
+       'album-name=s'   => sub { $albumname = $_[1]; $mode = "albumname"; },
+       'track-id=s'     => sub { $trackid = $_[1]; $mode = "track"; },
+       'recording-id=s' => sub { $recordingid = $_[1]; $mode = "recording"; },
 
-       'V|version'   => sub { print_version(); exit },
-       'H|help'      => sub { print_help(); exit },
+       'V|version'      => sub { print_version(); exit },
+       'H|help'         => sub { print_help(); exit },
 
-       'topmenu=s'   => \$topmenu, # top menu name (for submenu generation)
+       'topmenu=s'      => \$topmenu, # top menu name (for submenu generation)
 ) or do { print_usage; exit 1 };
 
+$mode = "albumname" if ($albumname && $mode eq "artist");
+
 unless (defined $menu) {
        $topmenu //= "MenuMPD";
        $menu = $topmenu . ($mode ne "top" ? $mode : "");
@@ -613,15 +677,17 @@ if ($mode eq "top") {
        }
 } elsif ($mode eq "artist") {
        # Create an artist menu.
-       my $matches = get_releases_by_artist_mbid(keys %artistids);
-       my $entry;
+       my ($matches, @recs) = get_releases_by_artist_mbid(keys %artistids);
 
        $menu //= "MenuMPDArtist";
 
        my @mbids = sort { datesort($matches, $a, $b) } keys %$matches;
        my @files = map { $matches->{$_}->{file} } @mbids;
        my @thumbs = get_item_thumbnails({ small => 1 }, @files);
-       fvwm_cmd("AddToMenu", $menu, "No releases found", "Title") unless @mbids;
+
+       unless (@mbids) {
+               fvwm_cmd("AddToMenu", $menu, "No releases found", "Title")
+       }
 
        foreach my $mbid (@mbids) {
                my $entry = $matches->{$mbid};
@@ -631,13 +697,53 @@ if ($mode eq "top") {
                                           "--album-id=$mbid");
                fvwm_cmd("AddToMenu", $menu,
                         $thumb . fvwm_label_escape($entry->{Album}),
-                        @submenu);
+                        @submenu);
+       }
+
+       my @artists = map { "--artist-id=$_" } keys %artistids;
+       my %nonalbums = map { $_->{Album} => $_ } @recs;
+       foreach my $name (sort keys %nonalbums) {
+               my $mbid = $nonalbums{$name}->{MUSICBRAINZ_TRACKID};
+               my @submenu = make_submenu("$topmenu-$mbid", @artists,
+                                          "--album-name=$name");
+               fvwm_cmd("AddToMenu", $menu, fvwm_label_escape($name), @submenu);
+       }
+} elsif ($mode eq "albumname") {
+       # Create a standalone recordings menu
+       my ($releases, @recs) = get_releases_by_artist_mbid(keys %artistids);
+
+       $menu //= "MenuMPDRecordings";
+       my @tracks = sort { $a->{Title} cmp $b->{Title} }
+                    grep { $_->{Album} eq $albumname } @recs;
+       my @notqueued = update_entry_ids(@tracks);
+
+       fvwm_cmd("AddToMenu", $menu);
+       fvwm_cmd("+", "No tracks found", "Title") unless @tracks;
+
+       foreach my $entry (@tracks) {
+               next unless exists $entry->{Id};
+
+               fvwm_cmd("+", menu_trackname($entry), "Exec",
+                        "exec", "$FindBin::Bin/mpdexec.pl",
+                        "playid", $entry->{Id});
+       }
+
+       fvwm_cmd("+", "Not in play queue", "Title") if @notqueued;
+       foreach my $entry (@notqueued) {
+               fvwm_cmd("+", menu_trackname($entry));
        }
-} elsif ($mode eq "track") {
-       my $matches = get_tracks_by_track_mbid($trackid);
+} elsif ($mode eq "track" || $mode eq "recording") {
+       my ($matches, $mbid);
        my @notqueued;
 
+       if ($mode eq "track") {
+               $matches = get_tracks_by_track_mbid($trackid)
+       } else {
+               $matches = get_tracks_by_recording_mbid($recordingid)
+       }
+
        $menu //= "MenuMPDTrack";
+       fvwm_cmd("DestroyMenu", $menu);
 
        my @files = sort { datesort($matches, $a, $b) } keys %$matches;
        my @thumbs = get_item_thumbnails({ small => 1 }, @files);