]> git.draconx.ca Git - mpdhacks.git/commitdiff
mpdmenu: Improve support for standalone recordings.
authorNick Bowler <nbowler@draconx.ca>
Wed, 28 Jul 2021 02:38:45 +0000 (22:38 -0400)
committerNick Bowler <nbowler@draconx.ca>
Thu, 29 Jul 2021 00:28:36 +0000 (20:28 -0400)
Standalone recordings have a slightly different metadata structure from
tracks associated with releases.  Let's handle them by presenting all the
standalone recordings for an artist together as if they were an actual
release by that artist.

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);